diff --git a/doc/dnf5.conf.5.rst b/doc/dnf5.conf.5.rst index 7bdad8b30..2a72e4cad 100644 --- a/doc/dnf5.conf.5.rst +++ b/doc/dnf5.conf.5.rst @@ -172,11 +172,11 @@ repository configuration file should aside from repo ID consists of baseurl, met ``group_package_types`` :ref:`list ` - List of the following: ``optional``, ``default``, ``mandatory``. + List of the following: ``optional``, ``default``, ``mandatory`` or ``conditional``. - Tells DNF5 which type of packages in groups will be installed when 'groupinstall' is called. + Tells DNF5 which type of packages in groups will be installed when 'group install' is called. - Default: ``default,mandatory``. + Default: ``default, mandatory, conditional``. .. _ignorearch_options-label: diff --git a/libdnf5/base/goal.cpp b/libdnf5/base/goal.cpp index 010322dd1..6d2afb189 100644 --- a/libdnf5/base/goal.cpp +++ b/libdnf5/base/goal.cpp @@ -1012,6 +1012,7 @@ GoalProblem Goal::Impl::add_replay_to_goal( for (const auto & group_replay : replay.groups) { libdnf5::GoalJobSettings settings_per_group = settings; settings_per_group.set_group_no_packages(true); + settings_per_group.set_group_package_types(group_replay.package_types); settings_per_group.set_group_search_groups(true); settings_per_group.set_group_search_environments(false); if (!group_replay.repo_id.empty()) { @@ -2469,8 +2470,6 @@ void Goal::Impl::add_group_remove_to_goal( void Goal::Impl::add_group_upgrade_to_goal( base::Transaction & transaction, comps::GroupQuery group_query, GoalJobSettings & settings) { auto & system_state = base->p_impl->get_system_state(); - auto & cfg_main = base->get_config(); - auto allowed_package_types = settings.resolve_group_package_types(cfg_main); comps::GroupQuery available_groups(base); available_groups.filter_installed(false); @@ -2497,18 +2496,19 @@ void Goal::Impl::add_group_upgrade_to_goal( continue; } auto available_group = available_group_query.get(); + auto state_group = system_state.get_group_state(group_id); + // upgrade the group itself rpm_goal.add_group( available_group, transaction::TransactionItemAction::UPGRADE, installed_group.get_reason(), - allowed_package_types); + state_group.package_types); if (settings.get_group_no_packages()) { continue; } - auto state_group = system_state.get_group_state(group_id); // set of package names that are part of the installed version of the group std::set old_set{}; diff --git a/libdnf5/base/transaction.cpp b/libdnf5/base/transaction.cpp index b39d32c86..e0ade60ca 100644 --- a/libdnf5/base/transaction.cpp +++ b/libdnf5/base/transaction.cpp @@ -1458,6 +1458,7 @@ std::string Transaction::serialize( group_replay.reason = group.get_reason(); // TODO(amatej): does each group has to have at least one repo? group_replay.repo_id = *(group.get_group().get_repos().begin()); + group_replay.package_types = group.get_package_types(); if (!comps_path.empty()) { group_replay.group_path = build_comps_xml_path(comps_path, xml_group.get_groupid()); diff --git a/libdnf5/transaction/transaction.cpp b/libdnf5/transaction/transaction.cpp index 3650328be..67e8fa087 100644 --- a/libdnf5/transaction/transaction.cpp +++ b/libdnf5/transaction/transaction.cpp @@ -350,6 +350,7 @@ std::string Transaction::serialize() { group_replay.action = group.get_action(); group_replay.reason = group.get_reason(); group_replay.repo_id = group.get_repoid(); + group_replay.package_types = group.get_package_types(); transaction_replay.groups.push_back(group_replay); } diff --git a/libdnf5/transaction/transaction_sr.cpp b/libdnf5/transaction/transaction_sr.cpp index 48562213f..bba285c11 100644 --- a/libdnf5/transaction/transaction_sr.cpp +++ b/libdnf5/transaction/transaction_sr.cpp @@ -78,10 +78,10 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t if (std::find_if(versions[1].begin(), versions[1].end(), [](unsigned char c) { return std::isdigit(c) == 0; }) != versions[1].end()) { - throw TransactionReplayError(M_("Invalid minor version: \"{}\", number expected."), versions[1]); + throw TransactionReplayError(M_("Invalid minor version: \"{}\", number expected"), versions[1]); } } else { - throw TransactionReplayError(M_("Missing key \"version\".")); + throw TransactionReplayError(M_("Missing key \"version\"")); } @@ -94,7 +94,7 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t std::string repo_id; if (json_object_is_type(json_environments, json_type_array) == 0) { - throw TransactionReplayError(M_("Unexpected type of \"environments\", array expected.")); + throw TransactionReplayError(M_("Unexpected type of \"environments\", array expected")); } for (std::size_t i = 0; i < json_object_array_length(json_environments); ++i) { @@ -102,12 +102,12 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t if (json_object_object_get_ex(environment, "id", &value) != 0) { environment_id = json_object_get_string(value); } else { - throw TransactionReplayError(M_("Missing object key \"id\" in an environment.")); + throw TransactionReplayError(M_("Missing object key \"id\" in an environment")); } if (json_object_object_get_ex(environment, "action", &value) != 0) { action = json_object_get_string(value); } else { - throw TransactionReplayError(M_("Missing object key \"action\" in an environment.")); + throw TransactionReplayError(M_("Missing object key \"action\" in an environment")); } if (json_object_object_get_ex(environment, "environment_path", &value) != 0) { environment_path = json_object_get_string(value); @@ -130,9 +130,11 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t std::string reason; std::string group_path; std::string repo_id; + std::string joined_package_types; + comps::PackageType package_types; if (json_object_is_type(json_groups, json_type_array) == 0) { - throw TransactionReplayError(M_("Unexpected type of \"groups\", array expected.")); + throw TransactionReplayError(M_("Unexpected type of \"groups\", array expected")); } for (std::size_t i = 0; i < json_object_array_length(json_groups); ++i) { @@ -140,17 +142,17 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t if (json_object_object_get_ex(group, "id", &value) != 0) { group_id = json_object_get_string(value); } else { - throw TransactionReplayError(M_("Missing object key \"id\" in a group.")); + throw TransactionReplayError(M_("Missing object key \"id\" in a group")); } if (json_object_object_get_ex(group, "action", &value) != 0) { action = json_object_get_string(value); } else { - throw TransactionReplayError(M_("Missing object key \"action\" in a group.")); + throw TransactionReplayError(M_("Missing object key \"action\" in a group")); } if (json_object_object_get_ex(group, "reason", &value) != 0) { reason = json_object_get_string(value); } else { - throw TransactionReplayError(M_("Missing object key \"reason\" in a group.")); + throw TransactionReplayError(M_("Missing object key \"reason\" in a group")); } if (json_object_object_get_ex(group, "group_path", &value) != 0) { group_path = json_object_get_string(value); @@ -158,13 +160,22 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t if (json_object_object_get_ex(group, "repo_id", &value) != 0) { repo_id = json_object_get_string(value); } + if (json_object_object_get_ex(group, "package_types", &value) != 0) { + joined_package_types = json_object_get_string(value); + auto package_types_vec = libdnf5::utils::string::split(joined_package_types, ","); + std::for_each(package_types_vec.begin(), package_types_vec.end(), libdnf5::utils::string::trim); + package_types = comps::package_type_from_string(package_types_vec); + } else { + package_types = comps::PackageType(); + } transaction_replay.groups.push_back( {transaction_item_action_from_string(action), transaction_item_reason_from_string(reason), group_id, group_path, - repo_id}); + repo_id, + package_types}); } } @@ -179,7 +190,7 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t std::string package_path; if (json_object_is_type(json_packages, json_type_array) == 0) { - throw TransactionReplayError(M_("Unexpected type of \"rpms\", array expected.")); + throw TransactionReplayError(M_("Unexpected type of \"rpms\", array expected")); } for (std::size_t i = 0; i < json_object_array_length(json_packages); ++i) { @@ -188,7 +199,7 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t nevra = json_object_get_string(value); // Verify we have a full nevra if (libdnf5::rpm::Nevra::parse(nevra, {libdnf5::rpm::Nevra::Form::NEVRA}).empty()) { - throw TransactionReplayError(M_("Cannot parse NEVRA for rpm \"{}\"."), nevra); + throw TransactionReplayError(M_("Cannot parse NEVRA for rpm \"{}\""), nevra); } } if (json_object_object_get_ex(package, "package_path", &value) != 0) { @@ -196,17 +207,17 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t } if (nevra.empty() && package_path.empty()) { throw TransactionReplayError( - M_("Either \"nevra\" or \"package_path\" object key is required in an rpm.")); + M_("Either \"nevra\" or \"package_path\" object key is required in an rpm")); } if (json_object_object_get_ex(package, "action", &value) != 0) { action = json_object_get_string(value); } else { - throw TransactionReplayError(M_("Missing object key \"action\" in an rpm.")); + throw TransactionReplayError(M_("Missing object key \"action\" in an rpm")); } if (json_object_object_get_ex(package, "reason", &value) != 0) { reason = json_object_get_string(value); } else { - throw TransactionReplayError(M_("Missing object key \"reason\" in an rpm.")); + throw TransactionReplayError(M_("Missing object key \"reason\" in an rpm")); } if (json_object_object_get_ex(package, "group_id", &value) != 0) { group_id = json_object_get_string(value); @@ -214,7 +225,7 @@ TransactionReplay parse_transaction_replay(const std::string & json_serialized_t if (reason == "Group" && action == "Reason Change") { throw TransactionReplayError( M_("Missing mandatory object key \"group_id\" in an rpm with reason \"Group\" and action " - "\"Reason Change\".")); + "\"Reason Change\"")); } } if (json_object_object_get_ex(package, "repo_id", &value) != 0) { @@ -282,6 +293,11 @@ std::string json_serialize(const TransactionReplay & transaction_replay) { json_object_object_add(json_group, "group_path", json_object_new_string(group.group_path.c_str())); } json_object_object_add(json_group, "repo_id", json_object_new_string(group.repo_id.c_str())); + json_object_object_add( + json_group, + "package_types", + json_object_new_string( + libdnf5::utils::string::join(package_types_to_strings(group.package_types), ", ").c_str())); json_object_array_add(json_groups, json_group); } json_object_object_add(root, "groups", json_groups); diff --git a/libdnf5/transaction/transaction_sr.hpp b/libdnf5/transaction/transaction_sr.hpp index e72ca076d..3807a4ed9 100644 --- a/libdnf5/transaction/transaction_sr.hpp +++ b/libdnf5/transaction/transaction_sr.hpp @@ -44,6 +44,7 @@ struct GroupReplay { // Path to serialized comps group relative to the transaction json file std::filesystem::path group_path; std::string repo_id; + libdnf5::comps::PackageType package_types; }; struct EnvironmentReplay { diff --git a/test/libdnf5/transaction/test_transaction_merge.cpp b/test/libdnf5/transaction/test_transaction_merge.cpp index e7f9d5e47..cf8852d3d 100644 --- a/test/libdnf5/transaction/test_transaction_merge.cpp +++ b/test/libdnf5/transaction/test_transaction_merge.cpp @@ -47,6 +47,7 @@ static void add_transaction_item_group( group_replay.action = action; group_replay.reason = TransactionItemReason::USER; group_replay.group_id = id; + group_replay.package_types = libdnf5::comps::PackageType::DEFAULT; trans.groups.push_back(group_replay); } @@ -96,7 +97,12 @@ void TransactionMergeTest::only_one_transaction() { {TransactionItemAction::INSTALL, TransactionItemReason::USER, "", "bash-4-1.x86_64", "", ""}}; CPPUNIT_ASSERT_EQUAL(expected, replay.packages); std::vector expected_groups = { - {TransactionItemAction::INSTALL, TransactionItemReason::USER, "vlc", "", ""}}; + {TransactionItemAction::INSTALL, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected_groups, replay.groups); std::vector expected_envs = { {TransactionItemAction::INSTALL, "basic-desktop-environment", "", ""}}; @@ -124,7 +130,12 @@ void TransactionMergeTest::two_disjoint() { {TransactionItemAction::INSTALL, TransactionItemReason::USER, "", "bash-4-1.x86_64", "", ""}}; CPPUNIT_ASSERT_EQUAL(expected, replay.packages); std::vector expected_groups = { - {TransactionItemAction::INSTALL, TransactionItemReason::USER, "vlc", "", ""}}; + {TransactionItemAction::INSTALL, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected_groups, replay.groups); std::vector expected_envs = { {TransactionItemAction::INSTALL, "basic-desktop-environment", "", ""}}; @@ -1374,7 +1385,13 @@ void TransactionMergeTest::group_install_install() { auto [replay, problems] = libdnf5::transaction::merge_transactions({trans1, trans2}, na_to_installed_nevras); - std::vector expected = {{TransactionItemAction::INSTALL, TransactionItemReason::USER, "vlc", "", ""}}; + std::vector expected = { + {TransactionItemAction::INSTALL, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected, replay.groups); CPPUNIT_ASSERT_EQUAL(std::vector(), problems); } @@ -1389,7 +1406,13 @@ void TransactionMergeTest::group_install_upgrade() { auto [replay, problems] = libdnf5::transaction::merge_transactions({trans1, trans2}, na_to_installed_nevras); - std::vector expected = {{TransactionItemAction::INSTALL, TransactionItemReason::USER, "vlc", "", ""}}; + std::vector expected = { + {TransactionItemAction::INSTALL, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected, replay.groups); CPPUNIT_ASSERT_EQUAL(std::vector(), problems); } @@ -1419,7 +1442,13 @@ void TransactionMergeTest::group_remove_upgrade() { auto [replay, problems] = libdnf5::transaction::merge_transactions({trans1, trans2}, na_to_installed_nevras); - std::vector expected = {{TransactionItemAction::INSTALL, TransactionItemReason::USER, "vlc", "", ""}}; + std::vector expected = { + {TransactionItemAction::INSTALL, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected, replay.groups); std::vector expected_problems = { {"Action 'Upgrade' 'vlc' cannot be merged because it is not present at that point -> " @@ -1437,7 +1466,13 @@ void TransactionMergeTest::group_remove_remove() { auto [replay, problems] = libdnf5::transaction::merge_transactions({trans1, trans2}, na_to_installed_nevras); - std::vector expected = {{TransactionItemAction::REMOVE, TransactionItemReason::USER, "vlc", "", ""}}; + std::vector expected = { + {TransactionItemAction::REMOVE, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected, replay.groups); std::vector expected_problems = { {"Action 'Remove' 'vlc' cannot be merged after it was 'Remove' in preceding " @@ -1455,7 +1490,13 @@ void TransactionMergeTest::group_upgrade_upgrade() { auto [replay, problems] = libdnf5::transaction::merge_transactions({trans1, trans2}, na_to_installed_nevras); - std::vector expected = {{TransactionItemAction::UPGRADE, TransactionItemReason::USER, "vlc", "", ""}}; + std::vector expected = { + {TransactionItemAction::UPGRADE, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected, replay.groups); CPPUNIT_ASSERT_EQUAL(std::vector(), problems); } @@ -1470,7 +1511,13 @@ void TransactionMergeTest::group_upgrade_remove() { auto [replay, problems] = libdnf5::transaction::merge_transactions({trans1, trans2}, na_to_installed_nevras); - std::vector expected = {{TransactionItemAction::REMOVE, TransactionItemReason::USER, "vlc", "", ""}}; + std::vector expected = { + {TransactionItemAction::REMOVE, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected, replay.groups); CPPUNIT_ASSERT_EQUAL(std::vector(), problems); } @@ -1485,7 +1532,13 @@ void TransactionMergeTest::group_upgrade_install() { auto [replay, problems] = libdnf5::transaction::merge_transactions({trans1, trans2}, na_to_installed_nevras); - std::vector expected = {{TransactionItemAction::INSTALL, TransactionItemReason::USER, "vlc", "", ""}}; + std::vector expected = { + {TransactionItemAction::INSTALL, + TransactionItemReason::USER, + "vlc", + "", + "", + libdnf5::comps::PackageType::DEFAULT}}; CPPUNIT_ASSERT_EQUAL(expected, replay.groups); CPPUNIT_ASSERT_EQUAL(std::vector(), problems); } diff --git a/test/shared/utils.hpp b/test/shared/utils.hpp index 860e6b163..d74432f14 100644 --- a/test/shared/utils.hpp +++ b/test/shared/utils.hpp @@ -22,6 +22,7 @@ along with libdnf. If not, see . #define TEST_LIBDNF5_UTILS_HPP #include "system/state.hpp" +#include "utils/string.hpp" #include #include @@ -303,17 +304,19 @@ struct assertion_traits { inline static bool equal( const libdnf5::transaction::GroupReplay & left, const libdnf5::transaction::GroupReplay & right) { return left.action == right.action && left.reason == right.reason && left.group_id == right.group_id && - left.group_path == right.group_path && left.repo_id == right.repo_id; + left.group_path == right.group_path && left.repo_id == right.repo_id && + left.package_types == right.package_types; } inline static std::string toString(const libdnf5::transaction::GroupReplay & replay) { return fmt::format( - "GroupReplay: group_id: {}, action: {}, reason: {}, repo_id: {}, group_path: {}", + "GroupReplay: group_id: {}, action: {}, reason: {}, repo_id: {}, group_path: {}, package_types: {}", replay.group_id, libdnf5::transaction::transaction_item_action_to_string(replay.action), libdnf5::transaction::transaction_item_reason_to_string(replay.reason), replay.repo_id, - std::string(replay.group_path)); + std::string(replay.group_path), + libdnf5::utils::string::join(package_types_to_strings(replay.package_types), ",")); } };