From 2a6064c28d271cdbdb16185b99c879b3a390c356 Mon Sep 17 00:00:00 2001 From: Petr Stodulka Date: Fri, 17 Apr 2026 15:58:18 +0200 Subject: [PATCH] IPU 9.9 -> 10.3: CTC1 candidate 1 - Bump leapp-framework to 6.5 - Change how commands are converted for text based report from the list representation - Introduce the format_list function for unified presentation of lists in reports and logs - Resolves: RHEL-156521 --- 0001-introduce-the-format_list-function.patch | 194 +++++++++++ ...-unsupported-upgrade-path-from-tests.patch | 61 ++++ ...ackit-add-new-upgrade-paths-to-tests.patch | 300 ++++++++++++++++++ ...update-actions-checkout-action-to-v6.patch | 48 +++ ...e-actions-github-script-action-to-v8.patch | 34 ++ ...e-peter-evans-create-or-update-comme.patch | 26 ++ ...correct-converting-of-list-based-com.patch | 157 +++++++++ ...fix-incorrect-converting-of-list-bas.patch | 171 ++++++++++ ...test-deps-update-pytest-requirements.patch | 37 +++ ...e-single-quoting-when-converting-rem.patch | 173 ++++++++++ leapp.spec | 31 +- plans/tier0.fmf | 8 +- 12 files changed, 1234 insertions(+), 6 deletions(-) create mode 100644 0001-introduce-the-format_list-function.patch create mode 100644 0002-packit-remove-unsupported-upgrade-path-from-tests.patch create mode 100644 0003-packit-add-new-upgrade-paths-to-tests.patch create mode 100644 0004-chore-deps-update-actions-checkout-action-to-v6.patch create mode 100644 0005-chore-deps-update-actions-github-script-action-to-v8.patch create mode 100644 0006-chore-deps-update-peter-evans-create-or-update-comme.patch create mode 100644 0007-reporting-fix-incorrect-converting-of-list-based-com.patch create mode 100644 0008-fixup-reporting-fix-incorrect-converting-of-list-bas.patch create mode 100644 0009-python-test-deps-update-pytest-requirements.patch create mode 100644 0010-fix-reporting-use-single-quoting-when-converting-rem.patch diff --git a/0001-introduce-the-format_list-function.patch b/0001-introduce-the-format_list-function.patch new file mode 100644 index 0000000..5a1bb50 --- /dev/null +++ b/0001-introduce-the-format_list-function.patch @@ -0,0 +1,194 @@ +From d67332c2234e437113493bfdce37594c1e2ae417 Mon Sep 17 00:00:00 2001 +From: Petr Stodulka +Date: Wed, 3 Dec 2025 08:48:08 +0100 +Subject: [PATCH 01/10] introduce the format_list function + +The construction of report messages involving lists (e.g., package +names, file paths) lacked a consistent formatting which led to +inconsistencies and redefinitions when creating reports. + +This patch introduces the format_list function to provide a consistent +formatting option across all reports. The function is available from both +leapp.libraries.stdlib and leapp.reporting. It supports custom sorting, +item limits, and configurable separator. + +Jira-ref: RHEL-126447 +--- + docs/source/best-practices.md | 17 +++++++++++ + leapp/libraries/stdlib/__init__.py | 36 +++++++++++++++++++++-- + leapp/reporting/__init__.py | 5 +++- + packaging/leapp.spec | 2 +- + tests/scripts/test_format_list.py | 47 ++++++++++++++++++++++++++++++ + 5 files changed, 103 insertions(+), 4 deletions(-) + create mode 100644 tests/scripts/test_format_list.py + +diff --git a/docs/source/best-practices.md b/docs/source/best-practices.md +index 11f861a..913dcbd 100644 +--- a/docs/source/best-practices.md ++++ b/docs/source/best-practices.md +@@ -155,6 +155,23 @@ In case of [StopActorExecutionError](leapp.exceptions.StopActorExecutionError) t + + You can also use the [StopActorExecution](leapp.exceptions.StopActorExecution) and [StopActorExecutionError](leapp.exceptions.StopActorExecutionError) exceptions inside a private or shared library. + ++## Consistent list formatting in reports ++ ++When constructing report messages that include lists of items (e.g. package names, file paths), use the `format_list` function to ensure consistent formatting across all reports. The function is available from both `leapp.libraries.stdlib` and `leapp.reporting`. It supports custom sorting, item limits, and configurable separators. ++ ++```python ++from leapp.reporting import format_list ++ ++pkgs = ['kernel', 'bash', 'glibc'] ++msg = 'The following packages will be removed:{}'.format(format_list(pkgs)) ++ ++# Output: ++#The following packages will be removed: ++# - bash ++# - glibc ++# - kernel ++``` ++ + ## Use the LEAPP and LEAPP\_DEVEL prefixes for new envars + + In case you need to change a behaviour of actor(s) for testing or development purposes - e.g. be able to skip a functionality in your actor - use environment variables. Such environment variables should start with prefix *LEAPP\_DEVEL*. Such variables are not possible to use on production systems without special *LEAPP\_UNSUPPORTED* variable. This prevents users to break their systems by a mistake. +diff --git a/leapp/libraries/stdlib/__init__.py b/leapp/libraries/stdlib/__init__.py +index 89e59b2..efe2029 100644 +--- a/leapp/libraries/stdlib/__init__.py ++++ b/leapp/libraries/stdlib/__init__.py +@@ -8,12 +8,15 @@ import logging + import os + import sys + import uuid ++from itertools import islice + + from leapp.exceptions import LeappError +-from leapp.utils.audit import create_audit_entry + from leapp.libraries.stdlib import api +-from leapp.libraries.stdlib.call import _call, STDOUT ++from leapp.libraries.stdlib.call import STDOUT, _call + from leapp.libraries.stdlib.config import is_debug ++from leapp.utils.audit import create_audit_entry ++ ++FMT_LIST_SEPARATOR = '\n - ' + + + class CalledProcessError(LeappError): +@@ -214,3 +217,32 @@ def run(args, split=False, callback_raw=_console_logging_handler, callback_lineb + ) + api.current_logger().debug('External command has finished: {0}'.format(str(args))) + return result ++ ++ ++def format_list(data, sep=FMT_LIST_SEPARATOR, callback_sort=sorted, limit=0): ++ """ ++ Format an iterable into a string using a specified separator that is prepended to every item. ++ ++ This function can be used to consistently format lists in reports, logs, and error messages. ++ ++ :param data: Iterable of items to format. ++ :type data: Iterable ++ :param sep: Separator prepended to each item. Defaults to FMT_LIST_SEPARATOR. ++ :type sep: str ++ :param callback_sort: Callable returning a new list, called before the limit is applied. ++ Set to None to preserve original order. Defaults to sorted. ++ :type callback_sort: Callable or None ++ :param limit: Maximum number of items to include. Defaults to 0 (no limit). ++ :type limit: int ++ :returns: A string with each item prefixed by the specified separator. ++ :rtype: str ++ """ ++ items = data ++ if callback_sort is not None: ++ items = callback_sort(data) ++ ++ if limit > 0: ++ items = islice(items, limit) ++ ++ res = ['{}{}'.format(sep, item) for item in items] ++ return ''.join(res) +diff --git a/leapp/reporting/__init__.py b/leapp/reporting/__init__.py +index 7a0e223..34af17a 100644 +--- a/leapp/reporting/__init__.py ++++ b/leapp/reporting/__init__.py +@@ -6,9 +6,12 @@ import os + import six + + from leapp.compat import string_types ++# NOTE(pstodulk): the format_list is imported to provide the function ++# also in this library. Its use is not planned here however. ++from leapp.libraries.stdlib import format_list ++from leapp.libraries.stdlib.api import produce + from leapp.models import fields, Model, ErrorModel + from leapp.topics import ReportTopic +-from leapp.libraries.stdlib.api import produce + from leapp.utils.deprecation import deprecated + + +diff --git a/packaging/leapp.spec b/packaging/leapp.spec +index 1e32cf6..a06b141 100644 +--- a/packaging/leapp.spec ++++ b/packaging/leapp.spec +@@ -13,7 +13,7 @@ + # This is kind of help for more flexible development of leapp repository, + # so people do not have to wait for new official release of leapp to ensure + # it is installed/used the compatible one. +-%global framework_version 6.2 ++%global framework_version 6.3 + + # IMPORTANT: everytime the requirements are changed, increment number by one + # - same for Provides in deps subpackage +diff --git a/tests/scripts/test_format_list.py b/tests/scripts/test_format_list.py +new file mode 100644 +index 0000000..1b0ca24 +--- /dev/null ++++ b/tests/scripts/test_format_list.py +@@ -0,0 +1,47 @@ ++import pytest ++ ++from leapp.libraries.stdlib import FMT_LIST_SEPARATOR, format_list ++ ++SEP = ', ' ++ ++ ++@pytest.mark.parametrize('data, kwargs, expected', [ ++ # Basic usage ++ ([], {}, ''), ++ (['c', 'a', 'b'], {}, '{0}a{0}b{0}c'.format(FMT_LIST_SEPARATOR)), ++ (['c', 'a', 'b'], {'sep': SEP}, ', a, b, c'), ++ (['a'], {'sep': SEP}, ', a'), ++ # Sorting ++ (['c', 'a', 'b'], {'sep': SEP, 'callback_sort': None}, ', c, a, b'), ++ (['c', 'a', 'b'], {'sep': SEP, 'callback_sort': lambda d: sorted(d, reverse=True)}, ', c, b, a'), ++ # Limit ++ (['c', 'a', 'b'], {'sep': SEP, 'limit': 2}, ', a, b'), ++ (['c', 'a', 'b'], {'sep': SEP, 'limit': 0}, ', a, b, c'), ++ (['b', 'a'], {'sep': SEP, 'limit': 10}, ', a, b'), ++ (['c', 'a', 'b'], {'sep': SEP, 'limit': -1}, ', a, b, c'), ++ # Non-list iterables ++ ({'a', 'b', 'c'}, {'sep': SEP, 'limit': 2}, ', a, b'), ++ (('a', 'b'), {'sep': SEP}, ', a, b'), ++ ({'b': 1, 'a': 2}, {'sep': SEP}, ', a, b'), ++ # Generators ++ ((x for x in ['c', 'a', 'b']), {'sep': SEP}, ', a, b, c'), ++ ((x for x in ['c', 'a', 'b']), {'sep': SEP, 'callback_sort': None, 'limit': 2}, ', c, a'), ++], ids=[ ++ 'empty_data', ++ 'single_item', ++ 'default_separator', ++ 'custom_separator', ++ 'no_sort', ++ 'reverse_sort', ++ 'limit', ++ 'limit_zero', ++ 'limit_larger_than_data', ++ 'negative_limit_ignored', ++ 'set_input', ++ 'tuple_input', ++ 'dict_keys_input', ++ 'generator_sorted', ++ 'generator_unsorted_with_limit', ++]) ++def test_format_list(data, kwargs, expected): ++ assert format_list(data, **kwargs) == expected +-- +2.53.0 + diff --git a/0002-packit-remove-unsupported-upgrade-path-from-tests.patch b/0002-packit-remove-unsupported-upgrade-path-from-tests.patch new file mode 100644 index 0000000..f497548 --- /dev/null +++ b/0002-packit-remove-unsupported-upgrade-path-from-tests.patch @@ -0,0 +1,61 @@ +From 2f210e763a8b851f1804f518fb02466db9cb55fa Mon Sep 17 00:00:00 2001 +From: Peter Mocary +Date: Tue, 3 Mar 2026 16:30:31 +0100 +Subject: [PATCH 02/10] packit: remove unsupported upgrade path from tests + +--- + .packit.yaml | 31 ------------------------------- + 1 file changed, 31 deletions(-) + +diff --git a/.packit.yaml b/.packit.yaml +index 0566801..254b24b 100644 +--- a/.packit.yaml ++++ b/.packit.yaml +@@ -326,15 +326,6 @@ jobs: + provisioning: + tags: + BusinessUnit: sst_upgrades@leapp_upstream_test +- - &tmt-env-settings-centos9torhel101 +- tmt: +- context: &tmt-context-centos9torhel101 +- distro: "centos-9" +- distro_target: "rhel-10.1" +- settings: +- provisioning: +- tags: +- BusinessUnit: sst_upgrades@leapp_upstream_test + - &tmt-env-settings-98to102 + tmt: + context: &tmt-context-98to102 +@@ -521,28 +512,6 @@ jobs: + # ###################### CentOS Stream > RHEL ########################## # + # ###################################################################### # + +-# ###################################################################### # +-# ############################ 9 > 10.1 ################################ # +-# ###################################################################### # +- +-- &sanity-centos9torhel101 +- <<: *sanity-abstract-9to10 +- trigger: pull_request +- identifier: sanity-CentOS9toRHEL10.1 +- targets: +- epel-9-x86_64: +- distros: [CentOS-Stream-9] +- tf_extra_params: +- test: +- tmt: +- plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm' +- environments: +- - artifacts: *artifacts-for-rhel9 +- - *tmt-env-settings-centos9torhel101 +- env: &env-centos9to101 +- SOURCE_RELEASE: "9" +- TARGET_RELEASE: "10.1" +- + # ###################################################################### # + # ############################ 9 > 10.2 ################################ # + # ###################################################################### # +-- +2.53.0 + diff --git a/0003-packit-add-new-upgrade-paths-to-tests.patch b/0003-packit-add-new-upgrade-paths-to-tests.patch new file mode 100644 index 0000000..a2f140c --- /dev/null +++ b/0003-packit-add-new-upgrade-paths-to-tests.patch @@ -0,0 +1,300 @@ +From 80c700223948b7a11af1b71c1ed81145662786ae Mon Sep 17 00:00:00 2001 +From: Peter Mocary +Date: Fri, 6 Mar 2026 17:56:10 +0100 +Subject: [PATCH 03/10] packit: add new upgrade paths to tests + +Adds the 8.10 -> 9.9 -> 10.3 upgrade paths to CI tests. +--- + .packit.yaml | 187 ++++++++++++++++++++++++++++++++++++++++++++++++--- + 1 file changed, 179 insertions(+), 8 deletions(-) + +diff --git a/.packit.yaml b/.packit.yaml +index 254b24b..cd45f27 100644 +--- a/.packit.yaml ++++ b/.packit.yaml +@@ -23,6 +23,7 @@ jobs: + get-current-version: + # get version from spec file instead from git tag + - bash -c "grep -m1 '^Version:' packaging/leapp.spec | grep -om1 '[0-9].[0-9.]**'" ++ + - job: copr_build + trigger: commit + metadata: +@@ -107,6 +108,15 @@ jobs: + provisioning: + tags: + BusinessUnit: sst_upgrades@leapp_upstream_test ++ - &tmt-env-settings-810to99 ++ tmt: ++ context: &tmt-context-810to99 ++ distro: "rhel-8.10" ++ distro_target: "rhel-9.9" ++ settings: ++ provisioning: ++ tags: ++ BusinessUnit: sst_upgrades@leapp_upstream_test + + - &sanity-abstract-8to9-aws + <<: *sanity-abstract-8to9 +@@ -188,7 +198,7 @@ jobs: + tf_extra_params: + test: + tmt: +- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm' ++ plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm' + environments: + - artifacts: *artifacts-for-rhel8 + - *tmt-env-settings-810to96 +@@ -217,7 +227,6 @@ jobs: + env: + <<: *env-810to96 + +- + # ###################################################################### # + # ############################# 8.10 > 9.8 ############################# # + # ###################################################################### # +@@ -268,13 +277,70 @@ jobs: + tf_extra_params: + test: + tmt: +- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true' ++ plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true' + environments: + - artifacts: *artifacts-for-rhel8 + - *tmt-env-settings-810to98 + env: + <<: *env-810to98 + ++# ###################################################################### # ++# ############################# 8.10 > 9.9 ############################# # ++# ###################################################################### # ++ ++- &sanity-810to99 ++ <<: *sanity-abstract-8to9 ++ trigger: pull_request ++ identifier: sanity-8.10to9.9 ++ tf_extra_params: ++ test: ++ tmt: ++ plan_filter: 'tag:8to9 & tag:tier0 & enabled:true' ++ environments: ++ - artifacts: *artifacts-for-rhel8 ++ - *tmt-env-settings-810to99 ++ env: &env-810to99 ++ SOURCE_RELEASE: "8.10" ++ TARGET_RELEASE: "9.9" ++ ++# On-demand minimal beaker tests ++- &beaker-minimal-810to99 ++ <<: *beaker-minimal-8to9-abstract-ondemand ++ trigger: pull_request ++ labels: ++ - beaker-minimal ++ - beaker-minimal-8.10to9.9 ++ - 8.10to9.9 ++ identifier: sanity-8.10to9.9-beaker-minimal-ondemand ++ tf_extra_params: ++ test: ++ tmt: ++ plan_filter: 'tag:8to9 & tag:partitioning & enabled:true' ++ environments: ++ - artifacts: *artifacts-for-rhel8 ++ - *tmt-env-settings-810to99 ++ env: ++ <<: *env-810to99 ++ ++# On-demand kernel-rt tests ++- &kernel-rt-810to99 ++ <<: *kernel-rt-abstract-8to9-ondemand ++ trigger: pull_request ++ labels: ++ - kernel-rt ++ - kernel-rt-8.10to9.9 ++ - 8.10to9.9 ++ identifier: sanity-8.10to9.9-kernel-rt-ondemand ++ tf_extra_params: ++ test: ++ tmt: ++ plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true' ++ environments: ++ - artifacts: *artifacts-for-rhel8 ++ - *tmt-env-settings-810to99 ++ env: ++ <<: *env-810to99 ++ + # ###################################################################### # + # ############################## 9 TO 10 ################################ # + # ###################################################################### # +@@ -344,6 +410,24 @@ jobs: + provisioning: + tags: + BusinessUnit: sst_upgrades@leapp_upstream_test ++ - &tmt-env-settings-99to103 ++ tmt: ++ context: &tmt-context-99to103 ++ distro: "rhel-9.9" ++ distro_target: "rhel-10.3" ++ settings: ++ provisioning: ++ tags: ++ BusinessUnit: sst_upgrades@leapp_upstream_test ++ - &tmt-env-settings-centos9torhel103 ++ tmt: ++ context: &tmt-context-centos9torhel103 ++ distro: "centos-9" ++ distro_target: "rhel-10.3" ++ settings: ++ provisioning: ++ tags: ++ BusinessUnit: sst_upgrades@leapp_upstream_test + + - &sanity-abstract-9to10-aws + <<: *sanity-abstract-9to10 +@@ -412,7 +496,7 @@ jobs: + tf_extra_params: + test: + tmt: +- plan_filter: 'tag:8to9 & tag:partitioning & enabled:true & tag:-rhsm' ++ plan_filter: 'tag:9to10 & tag:partitioning & enabled:true & tag:-rhsm' + environments: + - artifacts: *artifacts-for-rhel9 + - *tmt-env-settings-96to100 +@@ -431,7 +515,7 @@ jobs: + tf_extra_params: + test: + tmt: +- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm' ++ plan_filter: 'tag:9to10 & tag:kernel-rt & enabled:true & tag:-rhsm' + environments: + - artifacts: *artifacts-for-rhel9 + - *tmt-env-settings-96to100 +@@ -475,7 +559,7 @@ jobs: + tf_extra_params: + test: + tmt: +- plan_filter: 'tag:8to9 & tag:partitioning & enabled:true & tag:-rhsm' ++ plan_filter: 'tag:9to10 & tag:partitioning & enabled:true & tag:-rhsm' + environments: + - artifacts: *artifacts-for-rhel9 + - *tmt-env-settings-98to102 +@@ -497,13 +581,79 @@ jobs: + tf_extra_params: + test: + tmt: +- plan_filter: 'tag:8to9 & tag:kernel-rt & enabled:true & tag:-rhsm' ++ plan_filter: 'tag:9to10 & tag:kernel-rt & enabled:true & tag:-rhsm' + environments: + - artifacts: *artifacts-for-rhel9 + - *tmt-env-settings-98to102 + env: + <<: *env-98to102 + ++# ###################################################################### # ++# ############################# 9.9 > 10.3 ############################# # ++# ###################################################################### # ++ ++- &sanity-99to103 ++ <<: *sanity-abstract-9to10 ++ trigger: pull_request ++ identifier: sanity-9.9to10.3 ++ targets: ++ epel-9-x86_64: ++ distros: [RHEL-9.9.0-Nightly] ++ tf_extra_params: ++ test: ++ tmt: ++ plan_filter: 'tag:9to10 & tag:tier0 & enabled:true' ++ environments: ++ - artifacts: *artifacts-for-rhel9 ++ - *tmt-env-settings-99to103 ++ env: &env-99to103 ++ SOURCE_RELEASE: "9.9" ++ TARGET_RELEASE: "10.3" ++ ++# On-demand minimal beaker tests ++- &beaker-minimal-99to103 ++ <<: *beaker-minimal-9to10-abstract-ondemand ++ trigger: pull_request ++ labels: ++ - beaker-minimal ++ - beaker-minimal-9.9to10.3 ++ - 9.9to10.3 ++ identifier: sanity-9.9to10.3-beaker-minimal-ondemand ++ targets: ++ epel-9-x86_64: ++ distros: [RHEL-9.9-Nightly] ++ tf_extra_params: ++ test: ++ tmt: ++ plan_filter: 'tag:9to10 & tag:partitioning & enabled:true' ++ environments: ++ - artifacts: *artifacts-for-rhel9 ++ - *tmt-env-settings-99to103 ++ env: ++ <<: *env-99to103 ++ ++# On-demand kernel-rt tests ++- &kernel-rt-99to103 ++ <<: *kernel-rt-abstract-9to10-ondemand ++ trigger: pull_request ++ labels: ++ - kernel-rt ++ - kernel-rt-9.9to10.3 ++ - 9.9to10.3 ++ identifier: sanity-9.9to10.3-kernel-rt-ondemand ++ targets: ++ epel-9-x86_64: ++ distros: [RHEL-9.9-Nightly] ++ tf_extra_params: ++ test: ++ tmt: ++ plan_filter: 'tag:9to10 & tag:kernel-rt & enabled:true' ++ environments: ++ - artifacts: *artifacts-for-rhel9 ++ - *tmt-env-settings-99to103 ++ env: ++ <<: *env-99to103 ++ + # ###################################################################### # + # ########################## CentOS Stream ############################# # + # ###################################################################### # +@@ -527,7 +677,6 @@ jobs: + test: + tmt: + plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm' +- name: + environments: + - artifacts: *artifacts-for-rhel9 + - *tmt-env-settings-centos9torhel102 +@@ -535,6 +684,28 @@ jobs: + SOURCE_RELEASE: "9" + TARGET_RELEASE: "10.2" + ++# ###################################################################### # ++# ############################ 9 > 10.3 ################################ # ++# ###################################################################### # ++ ++- &sanity-centos9torhel103 ++ <<: *sanity-abstract-9to10 ++ trigger: pull_request ++ identifier: sanity-CentOS9toRHEL10.3 ++ targets: ++ epel-9-x86_64: ++ distros: [CentOS-Stream-9] ++ tf_extra_params: ++ test: ++ tmt: ++ plan_filter: 'tag:9to10 & tag:tier0 & enabled:true & tag:-rhsm' ++ environments: ++ - artifacts: *artifacts-for-rhel9 ++ - *tmt-env-settings-centos9torhel103 ++ env: &env-centos9torhel103 ++ SOURCE_RELEASE: "9" ++ TARGET_RELEASE: "10.3" ++ + # ###################################################################### # + # ################## CentOS Stream > CentOS Stream ##################### # + # ###################################################################### # +-- +2.53.0 + diff --git a/0004-chore-deps-update-actions-checkout-action-to-v6.patch b/0004-chore-deps-update-actions-checkout-action-to-v6.patch new file mode 100644 index 0000000..af91ea2 --- /dev/null +++ b/0004-chore-deps-update-actions-checkout-action-to-v6.patch @@ -0,0 +1,48 @@ +From 136ed3baac103bfb0945d74703c7b7a05f40d8bd Mon Sep 17 00:00:00 2001 +From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> +Date: Thu, 5 Mar 2026 09:51:22 +0000 +Subject: [PATCH 04/10] chore(deps): update actions/checkout action to v6 + +--- + .github/workflows/reuse-copr-build.yml | 4 ++-- + .github/workflows/unit-tests.yml | 2 +- + 2 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/.github/workflows/reuse-copr-build.yml b/.github/workflows/reuse-copr-build.yml +index c2fb51c..5e33fe3 100644 +--- a/.github/workflows/reuse-copr-build.yml ++++ b/.github/workflows/reuse-copr-build.yml +@@ -49,7 +49,7 @@ jobs: + # TODO: The correct way to checkout would be to use simmilar approach as in get_commit_by_timestamp function of + # the github gluetool module (i.e. do not use HEAD but the last commit before comment). + id: checkout +- uses: actions/checkout@v5 ++ uses: actions/checkout@v6 + with: + ref: "refs/pull/${{ steps.pr_nr.outputs.pr_nr }}/head" + +@@ -121,7 +121,7 @@ jobs: + - name: Checkout leapp-repository + id: checkout_leapp_repository + if: ${{ steps.leapp_repository_pr_regex_match.outputs.match != '' }} +- uses: actions/checkout@v5 ++ uses: actions/checkout@v6 + with: + repository: "oamg/leapp-repository" + ref: "refs/pull/${{ steps.leapp_repository_pr.outputs.leapp_repository_pr }}/head" +diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml +index 70d624d..906a45d 100644 +--- a/.github/workflows/unit-tests.yml ++++ b/.github/workflows/unit-tests.yml +@@ -36,7 +36,7 @@ jobs: + + steps: + - name: Checkout code +- uses: actions/checkout@v5 ++ uses: actions/checkout@v6 + with: + fetch-depth: '0' + - name: Set main to origin/main +-- +2.53.0 + diff --git a/0005-chore-deps-update-actions-github-script-action-to-v8.patch b/0005-chore-deps-update-actions-github-script-action-to-v8.patch new file mode 100644 index 0000000..64b3b19 --- /dev/null +++ b/0005-chore-deps-update-actions-github-script-action-to-v8.patch @@ -0,0 +1,34 @@ +From dc9e1c60e5cc84abf1b5df0972072f7795bc860c Mon Sep 17 00:00:00 2001 +From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> +Date: Thu, 5 Mar 2026 09:51:26 +0000 +Subject: [PATCH 05/10] chore(deps): update actions/github-script action to v8 + +--- + .github/workflows/reuse-copr-build.yml | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/.github/workflows/reuse-copr-build.yml b/.github/workflows/reuse-copr-build.yml +index 5e33fe3..0f6b823 100644 +--- a/.github/workflows/reuse-copr-build.yml ++++ b/.github/workflows/reuse-copr-build.yml +@@ -102,7 +102,7 @@ jobs: + - name: Add comment with copr build url + # TODO: Create comment when copr build fails. + id: link_copr +- uses: actions/github-script@v7 ++ uses: actions/github-script@v8 + with: + script: | + github.issues.createComment({ +@@ -160,7 +160,7 @@ jobs: + # TODO: Create comment when copr build fails. + id: link_copr_leapp_repository + if: ${{ steps.leapp_repository_pr_regex_match.outputs.match != '' }} +- uses: actions/github-script@v7 ++ uses: actions/github-script@v8 + with: + script: | + github.issues.createComment({ +-- +2.53.0 + diff --git a/0006-chore-deps-update-peter-evans-create-or-update-comme.patch b/0006-chore-deps-update-peter-evans-create-or-update-comme.patch new file mode 100644 index 0000000..abf6cad --- /dev/null +++ b/0006-chore-deps-update-peter-evans-create-or-update-comme.patch @@ -0,0 +1,26 @@ +From a2cbc6e98539926dc25862172b89a5585562009b Mon Sep 17 00:00:00 2001 +From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> +Date: Thu, 5 Mar 2026 09:51:28 +0000 +Subject: [PATCH 06/10] chore(deps): update + peter-evans/create-or-update-comment action to v5 + +--- + .github/workflows/pr-welcome-msg.yml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/.github/workflows/pr-welcome-msg.yml b/.github/workflows/pr-welcome-msg.yml +index 66d001e..5d9fc3e 100644 +--- a/.github/workflows/pr-welcome-msg.yml ++++ b/.github/workflows/pr-welcome-msg.yml +@@ -14,7 +14,7 @@ jobs: + runs-on: ubuntu-latest + steps: + - name: Create comment +- uses: peter-evans/create-or-update-comment@v4 ++ uses: peter-evans/create-or-update-comment@v5 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | +-- +2.53.0 + diff --git a/0007-reporting-fix-incorrect-converting-of-list-based-com.patch b/0007-reporting-fix-incorrect-converting-of-list-based-com.patch new file mode 100644 index 0000000..b239f7e --- /dev/null +++ b/0007-reporting-fix-incorrect-converting-of-list-based-com.patch @@ -0,0 +1,157 @@ +From 9fbd48b20f556cb613f3ca7999f4fc9d74e24024 Mon Sep 17 00:00:00 2001 +From: Peter Mocary +Date: Tue, 17 Mar 2026 12:22:35 +0100 +Subject: [PATCH 07/10] reporting: fix incorrect converting of list based + commands + +The reporting module handled conversion between shell command +representations for the text and json reports incorrectly. The list of +strings representation was converted without preserving the argument +separation correctly which results in malformed commands in the text +report. + +This patch utilizes the built-in shlex Python library for the conversion +which preserves the argument separation by wrapping arguments in quotes +when necessary. + +Jira: RHEL-156521 +--- + leapp/reporting/__init__.py | 9 +++-- + packaging/leapp.spec | 2 +- + tests/scripts/test_reporting.py | 61 +++++++++++++++++++++++++++++++-- + 3 files changed, 66 insertions(+), 6 deletions(-) + +diff --git a/leapp/reporting/__init__.py b/leapp/reporting/__init__.py +index 34af17a..ca2b80b 100644 +--- a/leapp/reporting/__init__.py ++++ b/leapp/reporting/__init__.py +@@ -1,8 +1,9 @@ + import datetime +-import json + import hashlib ++import json + import logging + import os ++import shlex + import six + + from leapp.compat import string_types +@@ -10,7 +11,7 @@ from leapp.compat import string_types + # also in this library. Its use is not planned here however. + from leapp.libraries.stdlib import format_list + from leapp.libraries.stdlib.api import produce +-from leapp.models import fields, Model, ErrorModel ++from leapp.models import ErrorModel, Model, fields + from leapp.topics import ReportTopic + from leapp.utils.deprecation import deprecated + +@@ -281,8 +282,10 @@ class RemediationCommand(BaseRemediation): + def __repr__(self): + # NOTE(ivasilev) As the message can contain non-ascii characters let's deal with it properly. + # As per python practices repr has to return an encoded string ++ # Note(pmocary) Using shlex.join is not possible here due to it being available from Python 3.8, ++ # thus we use the shlex.quote on each argument instead which is the same as shlex.join implementation. + return "[{}] {}".format(self._value['type'], +- ' '.join([_guarantee_encoded_str(c) for c in self._value['context']])) ++ ' '.join([shlex.quote(_guarantee_encoded_str(c)) for c in self._value['context']])) + + + class RemediationHint(BaseRemediation): +diff --git a/packaging/leapp.spec b/packaging/leapp.spec +index a06b141..ba906cf 100644 +--- a/packaging/leapp.spec ++++ b/packaging/leapp.spec +@@ -13,7 +13,7 @@ + # This is kind of help for more flexible development of leapp repository, + # so people do not have to wait for new official release of leapp to ensure + # it is installed/used the compatible one. +-%global framework_version 6.3 ++%global framework_version 6.4 + + # IMPORTANT: everytime the requirements are changed, increment number by one + # - same for Provides in deps subpackage +diff --git a/tests/scripts/test_reporting.py b/tests/scripts/test_reporting.py +index 2b0f245..8c284cb 100644 +--- a/tests/scripts/test_reporting.py ++++ b/tests/scripts/test_reporting.py +@@ -14,11 +14,10 @@ from leapp.reporting import ( + create_report_from_deprecation, + create_report_from_error, + _create_report_object, +- Audience, Flags, Groups, Key, RelatedResource, Remediation, Summary, Severity, Tags, Title ++ Audience, Flags, Groups, Key, RelatedResource, Remediation, RemediationCommand, Summary, Severity, Tags, Title + ) + from leapp.utils.report import generate_report_file + +- + ReportPrimitive = namedtuple('ReportPrimitive', ['data', 'path', 'value', 'is_leaf_list']) + + leaf_is_dict = ReportPrimitive({}, ('summary',), 'We better fix this!', False) +@@ -103,6 +102,64 @@ def test_report_tags_and_flags(): + assert Flags(["This is a new flag", Groups.INHIBITOR]).value == ["This is a new flag", "inhibitor"] + + ++@pytest.mark.parametrize('command, expected_command_form', [ ++ (['cmd'], "cmd"), ++ (['cmd', ''], "cmd ''"), ++ (['cmd', 'not_quoted'], "cmd not_quoted"), ++ (['ls', '-la', '/home/user'], "ls -la /home/user"), ++ ++ # Quoting ' ' ++ (['cmd', 'is quoted'], "cmd 'is quoted'"), ++ (['mv', '/home/user/test file', '/home/user/test_file'], "mv '/home/user/test file' /home/user/test_file"), ++ ++ # Quoting '$' ++ # Note: Execution of the first command would fail, but it is the correct conversion. ++ # The second test case should be used to utilize the shell expansion. ++ (['chsh', '-s', '$(which zsh)', '$USER'], "chsh -s '$(which zsh)' '$USER'"), ++ (['bash', '-c', 'chsh -s $(which zsh) $USER'], "bash -c 'chsh -s $(which zsh) $USER'"), ++ ++ # Quoting '\' ++ (['echo', '\\'], "echo '\\'"), ++ (['sed', '-i', 's/\\t/ /g', 'file.txt'], "sed -i 's/\\t/ /g' file.txt"), ++ ++ # Quoting '"' ++ (['echo', '"'], "echo '\"'"), ++ (['grep', 'key="value"', 'file.txt'], "grep 'key=\"value\"' file.txt"), ++ ++ # Quoting '`' ++ (['usermod', '-aG', 'wheel', '`whoami`'], "usermod -aG wheel '`whoami`'"), ++]) ++def test_remediation_command_repr_quoting(command, expected_command_form): ++ """Test that RemediationCommand.__repr__ properly quotes arguments with special characters.""" ++ remediation_command = RemediationCommand(value=command) ++ assert repr(remediation_command) == f'[command] {expected_command_form}' ++ ++ ++@pytest.mark.parametrize("report_suffix", ('.json', '.txt')) ++def test_remediation_command_in_report(report_suffix): ++ """Test that commands with spaces in arguments are preserved correctly in reports.""" ++ remediation_command_as_list = ["mv", "file with spaces.txt", "file_without_spaces.txt"] ++ remediation_command_as_string = "mv 'file with spaces.txt' file_without_spaces.txt" ++ ++ report_entries = [Title('Test title'), Summary('Test summary'), Audience('sysadmin')] ++ remediation = Remediation(commands=[remediation_command_as_list]) ++ report_entries.append(remediation) ++ ++ report_message = _create_report_object(report_entries).report ++ ++ with tempfile.NamedTemporaryFile(suffix=report_suffix) as reportfile: ++ generate_report_file([report_message], 'leapp-run-id', reportfile.name) ++ with open(reportfile.name) as f: ++ content = f.read() ++ if report_suffix == '.txt': ++ assert remediation_command_as_string in content ++ else: ++ report_data = json.loads(content) ++ remediations = report_data['entries'][0]['detail']['remediations'] ++ cmd_entry = [r for r in remediations if r['type'] == 'command'][0] ++ assert cmd_entry['context'] == remediation_command_as_list ++ ++ + @pytest.mark.parametrize("report_suffix", ('.json', '.txt')) + def test_remediation_with_non_ascii_value(report_suffix): + report_entries = [Title('Some report title'), Summary('Some summary not used for dynamical key generation'), +-- +2.53.0 + diff --git a/0008-fixup-reporting-fix-incorrect-converting-of-list-bas.patch b/0008-fixup-reporting-fix-incorrect-converting-of-list-bas.patch new file mode 100644 index 0000000..c816e4b --- /dev/null +++ b/0008-fixup-reporting-fix-incorrect-converting-of-list-bas.patch @@ -0,0 +1,171 @@ +From ad50bb2bdcc8a4b734c82f9d190dfe8c1611903a Mon Sep 17 00:00:00 2001 +From: Peter Mocary +Date: Wed, 25 Mar 2026 19:20:22 +0100 +Subject: [PATCH 08/10] fixup! reporting: fix incorrect converting of list + based commands + +--- + leapp/reporting/__init__.py | 34 ++++++++++++++++--- + tests/scripts/test_reporting.py | 60 ++++++++++++++++++++++++++++++--- + 2 files changed, 86 insertions(+), 8 deletions(-) + +diff --git a/leapp/reporting/__init__.py b/leapp/reporting/__init__.py +index ca2b80b..6bd0b49 100644 +--- a/leapp/reporting/__init__.py ++++ b/leapp/reporting/__init__.py +@@ -4,9 +4,11 @@ import json + import logging + import os + import shlex ++ + import six + + from leapp.compat import string_types ++ + # NOTE(pstodulk): the format_list is imported to provide the function + # also in this library. Its use is not planned here however. + from leapp.libraries.stdlib import format_list +@@ -273,6 +275,32 @@ def _guarantee_decoded_str(text, encoding='utf-8'): + return text + + ++def _quote_for_shell(s): ++ """ ++ Quote a string for shell usage as a literal. ++ ++ This function quotes the given string in single-quotes using the ++ shlex.quote. However, when the single-quote is present in the string it ++ instead uses double-quotes, resulting in cleaner output (e.g. "don't" ++ instead of 'don'"'"'t' that would be produced by shlex.quote). ++ ++ :param str s: String to quote for shell usage ++ :return str: Quoted string safe for shell usage ++ """ ++ ++ if "'" not in s: ++ return shlex.quote(s) ++ ++ # The string contains a single-quote, use double-quote style instead. ++ # Escape following characters to make it literal: \ " $ ` ! ++ escaped = (s.replace('\\', '\\\\') ++ .replace('"', '\\"') ++ .replace('$', '\\$') ++ .replace('`', '\\`') ++ .replace('!', '\\!')) ++ return '"{}"'.format(escaped) ++ ++ + class RemediationCommand(BaseRemediation): + def __init__(self, value=None): + if not isinstance(value, list): +@@ -282,10 +310,8 @@ class RemediationCommand(BaseRemediation): + def __repr__(self): + # NOTE(ivasilev) As the message can contain non-ascii characters let's deal with it properly. + # As per python practices repr has to return an encoded string +- # Note(pmocary) Using shlex.join is not possible here due to it being available from Python 3.8, +- # thus we use the shlex.quote on each argument instead which is the same as shlex.join implementation. +- return "[{}] {}".format(self._value['type'], +- ' '.join([shlex.quote(_guarantee_encoded_str(c)) for c in self._value['context']])) ++ quoted_command_args = [_quote_for_shell(_guarantee_encoded_str(c)) for c in self._value['context']] ++ return "[{}] {}".format(self._value['type'], ' '.join(quoted_command_args)) + + + class RemediationHint(BaseRemediation): +diff --git a/tests/scripts/test_reporting.py b/tests/scripts/test_reporting.py +index 8c284cb..2a4e724 100644 +--- a/tests/scripts/test_reporting.py ++++ b/tests/scripts/test_reporting.py +@@ -1,20 +1,31 @@ + # -*- coding: utf-8 -*- +-from collections import namedtuple + import datetime + import json + import os + import tempfile ++from collections import namedtuple + + import jsonschema + import pytest + + from leapp.reporting import ( +- _add_to_dict, + _DEPRECATION_SEVERITY_THRESHOLD, ++ Audience, ++ Flags, ++ Groups, ++ Key, ++ RelatedResource, ++ Remediation, ++ RemediationCommand, ++ Severity, ++ Summary, ++ Tags, ++ Title, ++ _add_to_dict, ++ _create_report_object, ++ _quote_for_shell, + create_report_from_deprecation, + create_report_from_error, +- _create_report_object, +- Audience, Flags, Groups, Key, RelatedResource, Remediation, RemediationCommand, Summary, Severity, Tags, Title + ) + from leapp.utils.report import generate_report_file + +@@ -102,6 +113,40 @@ def test_report_tags_and_flags(): + assert Flags(["This is a new flag", Groups.INHIBITOR]).value == ["This is a new flag", "inhibitor"] + + ++@pytest.mark.parametrize('string, expected_quoted', [ ++ # No quoting needed ++ ('path/to_the/file-name.txt', 'path/to_the/file-name.txt'), ++ ('user@host', 'user@host'), ++ ('value=123', 'value=123'), ++ ('a:b:c', 'a:b:c'), ++ ('99%', '99%'), ++ ('a+b', 'a+b'), ++ ++ # Single-quote style (no single quote in string) ++ ('', "''"), ++ ('file with spaces.txt', "'file with spaces.txt'"), ++ ('$(whoami)', "'$(whoami)'"), ++ ('$USER', "'$USER'"), ++ ('back`tick', "'back`tick'"), ++ ('double"quote', "'double\"quote'"), ++ ('exclaim!', "'exclaim!'"), ++ ('back\\slash', "'back\\slash'"), ++ ('new\nline', "'new\nline'"), ++ ('key="value"', "'key=\"value\"'"), ++ ++ # Double-quote style (a single quote in string) ++ ("It's fine", "\"It's fine\""), ++ ("It's `whoami`", "\"It's \\`whoami\\`\""), ++ ("User's home: $HOME", "\"User's home: \\$HOME\""), ++ ("Don't panic!", "\"Don't panic\\!\""), ++ ("s/'/\"/g", "\"s/'/\\\"/g\""), ++ ("sed -i 's/'/\"/g' file.txt", "\"sed -i 's/'/\\\"/g' file.txt\""), ++]) ++def test_quote_for_shell(string, expected_quoted): ++ """Test that _quote_for_shell properly quotes strings for shell usage.""" ++ assert _quote_for_shell(string) == expected_quoted ++ ++ + @pytest.mark.parametrize('command, expected_command_form', [ + (['cmd'], "cmd"), + (['cmd', ''], "cmd ''"), +@@ -128,6 +173,13 @@ def test_report_tags_and_flags(): + + # Quoting '`' + (['usermod', '-aG', 'wheel', '`whoami`'], "usermod -aG wheel '`whoami`'"), ++ ++ # Double-quote style when single quote is present ++ (['echo', "It's fine"], "echo \"It's fine\""), ++ (['mv', "user's file.txt", 'newfile.txt'], "mv \"user's file.txt\" newfile.txt"), ++ (['sed', '-i', "s/'/\"/g", 'file.txt'], "sed -i \"s/'/\\\"/g\" file.txt"), ++ (['bash', '-c', "echo \"user's home: $HOME\""], "bash -c \"echo \\\"user's home: \\$HOME\\\"\""), ++ (['grep', "can't find", '/var/log/some report.log'], "grep \"can't find\" '/var/log/some report.log'"), + ]) + def test_remediation_command_repr_quoting(command, expected_command_form): + """Test that RemediationCommand.__repr__ properly quotes arguments with special characters.""" +-- +2.53.0 + diff --git a/0009-python-test-deps-update-pytest-requirements.patch b/0009-python-test-deps-update-pytest-requirements.patch new file mode 100644 index 0000000..ec07ad9 --- /dev/null +++ b/0009-python-test-deps-update-pytest-requirements.patch @@ -0,0 +1,37 @@ +From bf931901cdc18275aed2023e5c37da0e246ae8a7 Mon Sep 17 00:00:00 2001 +From: Petr Stodulka +Date: Tue, 14 Apr 2026 14:21:39 +0200 +Subject: [PATCH 09/10] python test deps: update pytest requirements + +Originally we required pytest v6.2.5 for any python. This is +pretty old requirement coming from time of python ~ 3.8 and recently +a CVE-2025-71176 occuared, which is fix in pytest v9.0.3. +So let's update ranges to actually allow use of newer pytest on newer +systems: + + +----------------+---------------+ + | pytest version | python range | + +----------------+---------------+ + | 6.2.5 | 3.7- | + | 7.4.4 | 3.8.x - 3.9.x | + | 9.0.3 | 3.10+ | + +----------------+---------------+ +--- + requirements-tests.txt | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/requirements-tests.txt b/requirements-tests.txt +index d4501f2..5859a2a 100644 +--- a/requirements-tests.txt ++++ b/requirements-tests.txt +@@ -7,4 +7,6 @@ jsonschema==3.2.0 + mock + pylint + pytest-cov +-pytest==6.2.5 ++pytest==6.2.5; python_version < '3.8' ++pytest==7.4.4; python_version >= '3.8' and python_version < '3.10' ++pytest==9.0.3; python_version >= '3.10' +-- +2.53.0 + diff --git a/0010-fix-reporting-use-single-quoting-when-converting-rem.patch b/0010-fix-reporting-use-single-quoting-when-converting-rem.patch new file mode 100644 index 0000000..de6972e --- /dev/null +++ b/0010-fix-reporting-use-single-quoting-when-converting-rem.patch @@ -0,0 +1,173 @@ +From eaf4af1a549b3cb675f49f03d46d9d5de362753f Mon Sep 17 00:00:00 2001 +From: Peter Mocary +Date: Fri, 10 Apr 2026 21:51:42 +0200 +Subject: [PATCH 10/10] fix(reporting): use single quoting when converting + remediation commands + +The remediation commands are provided as a list of arguments that is +used in leapp-report.json. The same command is also converted to a +string that is used in leapp-report.txt and can be copy-pasted by the +user into shell. + +Originally, the command conversion tried to make the resulting commands +prettier by enabling double quoting whenever there was a single quote in +an argument, while still trying to make the double quoted argument a +literal. This proved to be problematic approach due to multiple cases +that resulted with incorrect commands. Namely, when the command called +`bash -c` with a subcommand that would be evaluated by another +non-interactive shell, or when there was an exclamation mark that would +have different meaning in non-interactive and interactive shell and its +escaping was not possible unless single-quotes are used. + +This patch makes the command conversion produce pretty commands in the +most cases, moreover, it makes sure the command is always correct (if +the list version is correct to begin with). This approach leaves +possibility to encounter unintuitive escaping in the resulting converted +command, although this can be prevented by carefully crafting the +command when creating the report. + +Note that if there is a subcommand called in the converted command, the +report creator is responsible for its form and for the best resulting +command it should favor the double quoting of its arguments when +possible. +--- + leapp/reporting/__init__.py | 28 +------------------ + packaging/leapp.spec | 2 +- + tests/scripts/test_reporting.py | 48 +++++---------------------------- + 3 files changed, 9 insertions(+), 69 deletions(-) + +diff --git a/leapp/reporting/__init__.py b/leapp/reporting/__init__.py +index 6bd0b49..43aa17d 100644 +--- a/leapp/reporting/__init__.py ++++ b/leapp/reporting/__init__.py +@@ -275,32 +275,6 @@ def _guarantee_decoded_str(text, encoding='utf-8'): + return text + + +-def _quote_for_shell(s): +- """ +- Quote a string for shell usage as a literal. +- +- This function quotes the given string in single-quotes using the +- shlex.quote. However, when the single-quote is present in the string it +- instead uses double-quotes, resulting in cleaner output (e.g. "don't" +- instead of 'don'"'"'t' that would be produced by shlex.quote). +- +- :param str s: String to quote for shell usage +- :return str: Quoted string safe for shell usage +- """ +- +- if "'" not in s: +- return shlex.quote(s) +- +- # The string contains a single-quote, use double-quote style instead. +- # Escape following characters to make it literal: \ " $ ` ! +- escaped = (s.replace('\\', '\\\\') +- .replace('"', '\\"') +- .replace('$', '\\$') +- .replace('`', '\\`') +- .replace('!', '\\!')) +- return '"{}"'.format(escaped) +- +- + class RemediationCommand(BaseRemediation): + def __init__(self, value=None): + if not isinstance(value, list): +@@ -310,7 +284,7 @@ class RemediationCommand(BaseRemediation): + def __repr__(self): + # NOTE(ivasilev) As the message can contain non-ascii characters let's deal with it properly. + # As per python practices repr has to return an encoded string +- quoted_command_args = [_quote_for_shell(_guarantee_encoded_str(c)) for c in self._value['context']] ++ quoted_command_args = [shlex.quote(_guarantee_encoded_str(c)) for c in self._value['context']] + return "[{}] {}".format(self._value['type'], ' '.join(quoted_command_args)) + + +diff --git a/packaging/leapp.spec b/packaging/leapp.spec +index ba906cf..0fde883 100644 +--- a/packaging/leapp.spec ++++ b/packaging/leapp.spec +@@ -13,7 +13,7 @@ + # This is kind of help for more flexible development of leapp repository, + # so people do not have to wait for new official release of leapp to ensure + # it is installed/used the compatible one. +-%global framework_version 6.4 ++%global framework_version 6.5 + + # IMPORTANT: everytime the requirements are changed, increment number by one + # - same for Provides in deps subpackage +diff --git a/tests/scripts/test_reporting.py b/tests/scripts/test_reporting.py +index 2a4e724..2010d29 100644 +--- a/tests/scripts/test_reporting.py ++++ b/tests/scripts/test_reporting.py +@@ -23,7 +23,6 @@ from leapp.reporting import ( + Title, + _add_to_dict, + _create_report_object, +- _quote_for_shell, + create_report_from_deprecation, + create_report_from_error, + ) +@@ -113,40 +112,6 @@ def test_report_tags_and_flags(): + assert Flags(["This is a new flag", Groups.INHIBITOR]).value == ["This is a new flag", "inhibitor"] + + +-@pytest.mark.parametrize('string, expected_quoted', [ +- # No quoting needed +- ('path/to_the/file-name.txt', 'path/to_the/file-name.txt'), +- ('user@host', 'user@host'), +- ('value=123', 'value=123'), +- ('a:b:c', 'a:b:c'), +- ('99%', '99%'), +- ('a+b', 'a+b'), +- +- # Single-quote style (no single quote in string) +- ('', "''"), +- ('file with spaces.txt', "'file with spaces.txt'"), +- ('$(whoami)', "'$(whoami)'"), +- ('$USER', "'$USER'"), +- ('back`tick', "'back`tick'"), +- ('double"quote', "'double\"quote'"), +- ('exclaim!', "'exclaim!'"), +- ('back\\slash', "'back\\slash'"), +- ('new\nline', "'new\nline'"), +- ('key="value"', "'key=\"value\"'"), +- +- # Double-quote style (a single quote in string) +- ("It's fine", "\"It's fine\""), +- ("It's `whoami`", "\"It's \\`whoami\\`\""), +- ("User's home: $HOME", "\"User's home: \\$HOME\""), +- ("Don't panic!", "\"Don't panic\\!\""), +- ("s/'/\"/g", "\"s/'/\\\"/g\""), +- ("sed -i 's/'/\"/g' file.txt", "\"sed -i 's/'/\\\"/g' file.txt\""), +-]) +-def test_quote_for_shell(string, expected_quoted): +- """Test that _quote_for_shell properly quotes strings for shell usage.""" +- assert _quote_for_shell(string) == expected_quoted +- +- + @pytest.mark.parametrize('command, expected_command_form', [ + (['cmd'], "cmd"), + (['cmd', ''], "cmd ''"), +@@ -174,12 +139,13 @@ def test_quote_for_shell(string, expected_quoted): + # Quoting '`' + (['usermod', '-aG', 'wheel', '`whoami`'], "usermod -aG wheel '`whoami`'"), + +- # Double-quote style when single quote is present +- (['echo', "It's fine"], "echo \"It's fine\""), +- (['mv', "user's file.txt", 'newfile.txt'], "mv \"user's file.txt\" newfile.txt"), +- (['sed', '-i', "s/'/\"/g", 'file.txt'], "sed -i \"s/'/\\\"/g\" file.txt"), +- (['bash', '-c', "echo \"user's home: $HOME\""], "bash -c \"echo \\\"user's home: \\$HOME\\\"\""), +- (['grep', "can't find", '/var/log/some report.log'], "grep \"can't find\" '/var/log/some report.log'"), ++ # Single quote in argument — shlex.quote uses the '"'"' pattern ++ (['echo', "It's fine"], "echo 'It'\"'\"'s fine'"), ++ (['mv', "user's file.txt", 'newfile.txt'], "mv 'user'\"'\"'s file.txt' newfile.txt"), ++ (['sed', '-i', "s/'/\"/g", 'file.txt'], "sed -i 's/'\"'\"'/\"/g' file.txt"), ++ (['bash', '-c', "echo \"user's home: $HOME\""], "bash -c 'echo \"user'\"'\"'s home: $HOME\"'"), ++ (['grep', "can't find", '/var/log/some report.log'], "grep 'can'\"'\"'t find' '/var/log/some report.log'"), ++ (['echo', "Don't panic!"], "echo 'Don'\"'\"'t panic!'"), + ]) + def test_remediation_command_repr_quoting(command, expected_command_form): + """Test that RemediationCommand.__repr__ properly quotes arguments with special characters.""" +-- +2.53.0 + diff --git a/leapp.spec b/leapp.spec index 097f5f2..9bb781e 100644 --- a/leapp.spec +++ b/leapp.spec @@ -7,7 +7,7 @@ # it. In case of upstream, dependencies are set differently, but YUM is not # capable enough to deal with them correctly all the time; we continue to use # simplified deps in RHEL to ensure that YUM can deal with it. -%global framework_version 6.2 +%global framework_version 6.5 # IMPORTANT: everytime the requirements are changed, increment number by one # - same for Provides in deps subpackage @@ -37,7 +37,7 @@ Name: leapp Version: 0.21.0 -Release: 1%{?dist} +Release: 2%{?dist} Summary: OS & Application modernization framework License: ASL 2.0 @@ -66,6 +66,17 @@ Requires: leapp-repository # PATCHES HERE # Patch0001: filename.patch +Patch0001: 0001-introduce-the-format_list-function.patch +Patch0002: 0002-packit-remove-unsupported-upgrade-path-from-tests.patch +Patch0003: 0003-packit-add-new-upgrade-paths-to-tests.patch +Patch0004: 0004-chore-deps-update-actions-checkout-action-to-v6.patch +Patch0005: 0005-chore-deps-update-actions-github-script-action-to-v8.patch +Patch0006: 0006-chore-deps-update-peter-evans-create-or-update-comme.patch +Patch0007: 0007-reporting-fix-incorrect-converting-of-list-based-com.patch +Patch0008: 0008-fixup-reporting-fix-incorrect-converting-of-list-bas.patch +Patch0009: 0009-python-test-deps-update-pytest-requirements.patch +Patch0010: 0010-fix-reporting-use-single-quoting-when-converting-rem.patch + %description Leapp utility provides the possibility to use the Leapp framework via CLI. The utility itself does not define any subcommands but "help". All leapp @@ -161,6 +172,16 @@ Requires: findutils # APPLY REGISTERED PATCHES HERE # %%patch -P 0001 -p1 +%patch -P 0001 -p1 +%patch -P 0002 -p1 +%patch -P 0003 -p1 +%patch -P 0004 -p1 +%patch -P 0005 -p1 +%patch -P 0006 -p1 +%patch -P 0007 -p1 +%patch -P 0008 -p1 +%patch -P 0009 -p1 +%patch -P 0010 -p1 ################################################## # Build @@ -246,6 +267,12 @@ install -m 0644 -p man/leapp.1 %{buildroot}%{_mandir}/man1/ %changelog +* Fri Apr 17 2026 Petr Stodulka - 0.21.0-2 +- Bump leapp-framework to 6.5 +- Change how commands are converted for text based report from the list representation +- Introduce the format_list function for unified presentation of lists in reports and logs +- Resolves: RHEL-156521 + * Tue Feb 10 2026 Karolina Kula - 0.21.0-1 - Rebase to new upstream 0.21.0 - Resolves: RHEL-128269 diff --git a/plans/tier0.fmf b/plans/tier0.fmf index 759dec5..a323c1a 100644 --- a/plans/tier0.fmf +++ b/plans/tier0.fmf @@ -5,12 +5,12 @@ summary: Internal Tier0 tests environment: - SOURCE_RELEASE: '9.8' - TARGET_RELEASE: '10.2' + SOURCE_RELEASE: '9.9' + TARGET_RELEASE: '10.3' context: - distro: rhel-9.8 - distro_target: rhel-10.2 + distro: rhel-9.9 + distro_target: rhel-10.3 adjust: - enabled: false