From f8f749a0a1086cea8eb6815b60cf3cafcfa0dd319812a1e89e0aad3d086406ff Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Mon, 12 Jan 2026 11:31:43 +0000 Subject: [PATCH 1/2] Release 1.3.1 OBS-URL: https://build.opensuse.org/package/show/devel:sap:ansible/ansible-sap-launchpad?expand=0&rev=11 --- .gitattributes | 23 ++ .gitignore | 1 + _service | 3 + ansible-sap-launchpad-1.2.1.tar.gz | 3 + ansible-sap-launchpad-1.3.1.tar.gz | 3 + ansible-sap-launchpad.changes | 42 +++ ansible-sap-launchpad.rpmlintrc | 6 + ansible-sap-launchpad.spec | 68 ++++ ansible-sap-launchpad.yaml | 20 ++ transformation.py | 544 +++++++++++++++++++++++++++++ 10 files changed, 713 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 _service create mode 100644 ansible-sap-launchpad-1.2.1.tar.gz create mode 100644 ansible-sap-launchpad-1.3.1.tar.gz create mode 100644 ansible-sap-launchpad.changes create mode 100644 ansible-sap-launchpad.rpmlintrc create mode 100644 ansible-sap-launchpad.spec create mode 100644 ansible-sap-launchpad.yaml create mode 100644 transformation.py diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/_service b/_service new file mode 100644 index 0000000..e2ab7b1 --- /dev/null +++ b/_service @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/ansible-sap-launchpad-1.2.1.tar.gz b/ansible-sap-launchpad-1.2.1.tar.gz new file mode 100644 index 0000000..8d203b2 --- /dev/null +++ b/ansible-sap-launchpad-1.2.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f2114ac48614215c2a304bc6f2a9e334a399f953cd6204977f3b0b23fd4f5024 +size 49284 diff --git a/ansible-sap-launchpad-1.3.1.tar.gz b/ansible-sap-launchpad-1.3.1.tar.gz new file mode 100644 index 0000000..d87da86 --- /dev/null +++ b/ansible-sap-launchpad-1.3.1.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebcaefac31227e1406b9b1e6c342dc3c8855db9ec262f56278129099f5b85227 +size 58265 diff --git a/ansible-sap-launchpad.changes b/ansible-sap-launchpad.changes new file mode 100644 index 0000000..351b77b --- /dev/null +++ b/ansible-sap-launchpad.changes @@ -0,0 +1,42 @@ +------------------------------------------------------------------- +Mon Jan 12 10:48:11 UTC 2026 - Marcel Mamula + +Refactor Ansible Modules and adjust for ansible-core 2.19. + +- 1.3.1 + - Bugfixes: + - collection: Add ansible-test sanity workflow and fix sanity errors + +- 1.3.0 + - Changes: + - collection: Refactor all Ansible Modules + - sap_software_download: Update for ansible-core 2.19 + - Bugfixes: + - sap_software_download: Fix for failed checksums not correctly retrying + +------------------------------------------------------------------- +Tue Jul 8 13:29:05 UTC 2025 - Marcel Mamula + +- 1.2.1 + - Bugfixes: + - software_center_download: Improved logic for skipping existing files and getting valid filename + - sap_software_download: Add SLES 16 python313 support and update changelog + +------------------------------------------------------------------- +Mon Apr 7 10:28:18 UTC 2025 - Marcel Mamula + +- 1.2.0 + - Changes: + - sap_software_download: New Ansible Role with enhanced logic for downloading software using Ansible Module software_center_download + - sap_software_download: Download stack XML option + - software_center_download: Add option to search for latest packages + - maintenance_planner modules: Add option to use Display ID instead of name + - Collection Readme update and preparation for 1.2.0 release + - Bugfixes: + - fix: cache gigya sdk build number + +------------------------------------------------------------------- +Fri Jan 20 09:00:00 UTC 2025 - Marcel Mamula + +- 1.1.1 + - Initial release. diff --git a/ansible-sap-launchpad.rpmlintrc b/ansible-sap-launchpad.rpmlintrc new file mode 100644 index 0000000..6d8a1fb --- /dev/null +++ b/ansible-sap-launchpad.rpmlintrc @@ -0,0 +1,6 @@ +# rpmlint configuration for ansible-sap-launchpad package. + +# Ansible modules are executed by the Ansible engine on managed nodes, not directly +# as scripts on the control node. The shebang is retained for backwards +# compatibility, but the executable bit is intentionally omitted. +addFilter("E: non-executable-script .*/community/sap_launchpad/plugins/modules/.*\.py .*") diff --git a/ansible-sap-launchpad.spec b/ansible-sap-launchpad.spec new file mode 100644 index 0000000..3036bb0 --- /dev/null +++ b/ansible-sap-launchpad.spec @@ -0,0 +1,68 @@ +# +# spec file for package ansible-sap-launchpad +# +# Copyright (c) 2026 SUSE LLC and contributors +# +# All modifications and additions to the file contributed by third parties +# remain the property of their copyright owners, unless otherwise agreed +# upon. The license for this file, and modifications and additions to the +# file, is the same license as for the pristine package itself (unless the +# license for the pristine package is not an Open Source License, in which +# case the license is the MIT License). An "Open Source License" is a +# license that conforms to the Open Source Definition (Version 1.9) +# published by the Open Source Initiative. + +# Please submit bugfixes or comments via https://bugs.opensuse.org/ +# + + +%define ansible_collection_name community-sap_launchpad-%{version} + +Name: ansible-sap-launchpad +Summary: Ansible collection community.sap_launchpad for SAP Automation +License: Apache-2.0 +Version: 1.3.1 +Release: 0 +URL: https://github.com/sap-linuxlab/community.sap_launchpad/ +Source0: %{url}archive/refs/tags/%{version}.tar.gz#/%{name}-%{version}.tar.gz +Source1: %{name}.yaml +Source2: transformation.py + +BuildArch: noarch + + +Requires: ansible >= 9 +Requires: ansible-core >= 2.16 +BuildRequires: ansible >= 9 +BuildRequires: ansible-core >= 2.16 + +# Python module ruamel.yaml for collection-update.py +BuildRequires: python3-ruamel.yaml + +%description +This package provides a Ansible collection community.sap_launchpad. + +It automates download of SAP Software for SAP installations. +Downloads software using list of files or maintenance plan ID. + +%prep +%autosetup -p1 -n community.sap_launchpad-%{version} + +%build +# Execute python script to update documentation and remove unsupported roles +python3 %{_sourcedir}/transformation.py --config %{_sourcedir}/%{name}.yaml --build_dir . +# Ensure there are no executable files present, except for sh and py files. +find . -type f -perm /u+x,g+x,o+x -not -name "*.sh" -not -name "*.py" -exec chmod -x {} \; +# Build the Ansible collection +ansible-galaxy collection build --output-path %{_builddir} + +%install +# ansible-galaxy always appends ansible_collections folder into collections path +mkdir -p %{buildroot}%{_datadir}/ansible/collections +ansible-galaxy collection install --force %{_builddir}/%{ansible_collection_name}.tar.gz \ + --collections-path %{buildroot}%{_datadir}/ansible/collections + +%files +%{_datadir}/ansible/collections/ + +%changelog diff --git a/ansible-sap-launchpad.yaml b/ansible-sap-launchpad.yaml new file mode 100644 index 0000000..7a860ec --- /dev/null +++ b/ansible-sap-launchpad.yaml @@ -0,0 +1,20 @@ +# Configuration file for changes before build. + +# Types of available options for transformation: +# - remove_paths - Removes the specified files or directories. +# - remove_lines - Removes lines matching the patterns from the files matching specified glob patterns. +# - replace_text - Replaces text in files matching specified glob patterns. +# - update_yaml_key - Updates a specific key's value in the specified YAML files matching specified glob patterns. +# - append_yaml_list - Appends a new item to a list in the specified YAML files matching specified glob patterns. +# - set_yaml_value - Sets or replaces a specific key's value in the specified YAML files matching specified glob patterns. +# - remove_yaml_list_items - Removes specific items from a list in the specified YAML files matching specified glob patterns. + +changes: + - type: remove_paths + # Remove all 'meta/main.yml' files within any role directory. + # Remove private .git/.gitkeep files and changelog generator configuration. + paths: + - ".git*" + - "*/.git*" + - "*/*/.git*" + - "changelogs/.plugin-cache.yaml" diff --git a/transformation.py b/transformation.py new file mode 100644 index 0000000..8b77079 --- /dev/null +++ b/transformation.py @@ -0,0 +1,544 @@ +# This Python script updates upstream with downstream customizations before packaging. +# All customizations are defined in separate yaml file. +# List of available customizations: +# - remove_paths - Removes the specified files or directories. +# - remove_lines - Removes lines matching the patterns from the files matching specified glob patterns. +# - replace_text - Replaces text in files matching specified glob patterns. +# - update_yaml_key - Updates a specific key's value in the specified YAML files matching specified glob patterns. +# - append_yaml_list - Appends a new item to a list in the specified YAML files matching specified glob patterns. +# - set_yaml_value - Sets or replaces a specific key's value in the specified YAML files matching specified glob patterns. +# - remove_yaml_list_items - Removes specific items from a list in the specified YAML files matching specified glob patterns. + +# Intended use: Executed by spec file during %prep + +import os +import shutil +import re +import glob +import argparse + +# Requires entry in spec file: BuildRequires: python3-ruamel.yaml +from ruamel.yaml import YAML + +yaml = YAML() +yaml.preserve_quotes = True # Preserves quotes +yaml.indent(mapping=2, sequence=4, offset=2) # Preserves indents in yaml +yaml.width = 4096 # Disable ruamel wrapping long lines + + +def load_config(config_file): + """Loads the configuration from a YAML file. + """ + with open(config_file, 'r') as f: + return yaml.load(f) + + +def remove_paths(build_dir, paths): + """Removes the specified files or directories. + + This function iterates through a list of path patterns, finds all matching + files and directories using glob, and removes them. This is useful for + cleaning up a build directory by removing unnecessary files or directories + before packaging. + + Args: + build_dir (str): The absolute path to the base directory from which + paths will be removed. + paths (list[str]): A list of path patterns to remove. These paths are + relative to `build_dir` and can include glob patterns (e.g., '*.txt', + 'temp/**'). + + Example: + ```yaml + changes: + - type: remove_paths + paths: + - "roles/*/meta/main.yml" + ``` + """ + + for path_pattern in paths: + full_pattern = os.path.join(build_dir, path_pattern) + for item in glob.glob(full_pattern): + relative_path = os.path.relpath(item, build_dir) + if os.path.isdir(item): + print(f"Removed: {relative_path}") + shutil.rmtree(item, ignore_errors=True) + elif os.path.isfile(item): + print(f"Removed: {relative_path}") + os.remove(item) + + +def remove_lines(build_dir, files_patterns, patterns): + """Removes lines matching the patterns from the files matching specified glob patterns. + + This function finds files matching the given glob patterns and then reads + each file to remove any lines that match any of the specified regular + expression patterns. This is useful for cleaning up documentation or + configuration files by removing obsolete or unwanted content. + + Args: + build_dir (str): The absolute path to the base directory where the + files are located. + files_patterns (list[str]): A list of file path patterns to process. + These paths are relative to `build_dir` and support glob + patterns (e.g., 'docs/*.md', 'roles/**/tasks/main.yml'). + patterns (list[str]): A list of regular expression patterns. Any line + in the target files that matches one of these patterns will be + removed. + + Example: + ```yaml + changes: + - type: remove_lines + files: + - "README.md" + - "roles/*/README.md" + patterns: + - "\\[Ansible Lint" + ``` + """ + + for file_pattern in files_patterns: + full_pattern = os.path.join(build_dir, file_pattern) + for file_path in glob.glob(full_pattern): + if os.path.isfile(file_path): + relative_path = os.path.relpath(file_path, build_dir) + with open(file_path, 'r') as f: + lines = f.readlines() + updated_lines = [ + line + for line in lines + if not any(re.search(pattern, line) for pattern in patterns) + ] + with open(file_path, 'w') as f: + f.writelines(updated_lines) + print(f"Removed lines from: {relative_path}") + + +def replace_text(build_dir, files_patterns, replacements): + """Replaces text in files matching specified glob patterns. + + + This function finds all files matching the given patterns and performs a + series of find-and-replace operations on their content. The 'find' + patterns are treated as regular expressions, allowing for powerful and + flexible text manipulation. This is useful for updating project-wide + values like namespaces, repository URLs, or other boilerplate text. + + Args: + build_dir (str): The absolute path to the base directory where the + files are located. + files_patterns (list[str]): A list of file path patterns to process. + These paths are relative to `build_dir` and support glob + patterns (e.g., 'README.md', 'roles/**/*.yml'). + replacements (list[dict[str, str]]): A list of replacement rules. + Each rule is a dictionary with two keys: + - 'find': The regular expression pattern to search for. + - 'replace': The string to substitute for each match. + + Example: + ```yaml + changes: + - type: replace_text + files: + - "galaxy.yml" + replacements: + - { find: "namespace: community", replace: "namespace: suse" } + ``` + """ + + for file_pattern in files_patterns: + full_pattern = os.path.join(build_dir, file_pattern) + for file_path in glob.glob(full_pattern): + if os.path.isfile(file_path): + relative_path = os.path.relpath(file_path, build_dir) + with open(file_path, 'r') as f: + content = f.read() + for replacement in replacements: + content = re.sub( + replacement['find'], + replacement['replace'], + content, + flags=re.MULTILINE, + ) + with open(file_path, 'w') as f: + f.write(content) + print( + f"Replaced text {replacement['find']} with {replacement['replace']} in: {relative_path}" + ) + + +def read_yaml_header(file_path): + """Reads the header of a YAML file (lines before the first '---'). + + Args: + file_path (str): The path to the YAML file. + + Returns: + list: A list of lines representing the header of the YAML file. + """ + header_lines = [] + with open(file_path, 'r') as f: + for line in f: + if line.strip() == '---': + header_lines.append(line) + break + header_lines.append(line) + return header_lines + + +def update_yaml_key(build_dir, files_patterns, key_path, value): + """Updates a specific key's value in the specified YAML files, preserving the header. + + This function finds all YAML files matching the given patterns and updates + the value of a specified key. The key can be nested, using dot notation + for the path (e.g., 'parent.child.key'). If the key or any part of its + path does not exist, it will be created. + + The function is designed to preserve YAML formatting, including comments + and indentation, by using `ruamel.yaml` and by explicitly handling + non-YAML headers (lines before the first '---'). + + Args: + build_dir (str): The absolute path to the base directory where the + YAML files are located. + files_patterns (list[str]): A list of file path patterns to process, + relative to `build_dir`. Supports glob patterns (e.g., 'galaxy.yml', + 'roles/**/meta.yml'). + key_path (str): The dot-separated path to the key to update + (e.g., 'info.version', 'authors'). + value (any): The new value to assign to the key. This can be any + YAML-compatible type (string, list, dictionary, etc.). + + Example: + ```yaml + changes: + - type: update_yaml_key + files: + - "galaxy.yml" + key: "build_ignore" + value: + - "tests" + ``` + """ + + for file_pattern in files_patterns: + full_pattern = os.path.join(build_dir, file_pattern) + for file_path in glob.glob(full_pattern): + if os.path.exists(file_path): + try: + relative_path = os.path.relpath(file_path, build_dir) + header_lines = read_yaml_header(file_path) + with open(file_path, 'r') as f: + # Skip header lines when loading + for _ in header_lines: + next(f, None) + data = yaml.load(f) + + keys = key_path.split('.') + current_level = data + for i, key in enumerate(keys): + if i == len(keys) - 1: + if key not in current_level: + print( + f"Warning: Key '{key_path}' not found in {relative_path}" + ) + else: + current_level[key] = value + print(f"Updated key '{key_path}' in: {relative_path}") + else: + if key not in current_level: + current_level[key] = {} + print( + f"Created missing key '{key}' in path '{key_path}' in: {relative_path}" + ) + current_level = current_level[key] + + with open(file_path, 'w') as f: + f.writelines(header_lines) + yaml.dump(data, f) + + except Exception as e: + print(f"Error processing YAML in {relative_path}: {e}") + + +def append_yaml_list(build_dir, files_patterns, list_key_path, new_item): + """Appends a new item to a list in the specified YAML files, preserving the header. + + This function finds all YAML files matching the given patterns and appends + a new item to a specified list. The list is identified by a dot-separated + key path. If the list or any part of its path does not exist, it will be + created. The function ensures the item is not added if it already exists + in the list, preventing duplicates. + + Like other YAML modification functions in this script, it preserves + comments and formatting by using `ruamel.yaml` and handling file headers. + + Args: + build_dir (str): The absolute path to the base directory where the + YAML files are located. + files_patterns (list[str]): A list of file path patterns to process, + relative to `build_dir`. Supports glob patterns. + list_key_path (str): The dot-separated path to the list to which the + item will be appended (e.g., 'galaxy_info.platforms'). + new_item (any): The item to append to the list. This can be a simple + string or a more complex dictionary. + + Example: + ```yaml + changes: + - type: append_yaml_list + files: + - "roles/*/meta/main.yml" + list_key: "galaxy_info.platforms" + new_item: + name: "SLES" + versions: + - "15" + ``` + """ + + for file_pattern in files_patterns: + full_pattern = os.path.join(build_dir, file_pattern) + for file_path in glob.glob(full_pattern): + if os.path.exists(file_path): + try: + relative_path = os.path.relpath(file_path, build_dir) + header_lines = read_yaml_header(file_path) + with open(file_path, 'r') as f: + # Skip header lines when loading + for _ in header_lines: + next(f, None) + data = yaml.load(f) + + keys = list_key_path.split('.') + current_level = data + for i, key in enumerate(keys): + if i == len(keys) - 1: + if key not in current_level: + current_level[key] = [] + print( + f"Created missing list '{list_key_path}' in: {relative_path}" + ) + if isinstance(current_level[key], list): + if new_item not in current_level[key]: + current_level[key].append(new_item) + print( + f"Appended item to list '{list_key_path}' in: {relative_path}" + ) + else: + print( + f"Item '{new_item}' already exists in list '{list_key_path}' in: {relative_path}" + ) + else: + if key not in current_level: + current_level[key] = {} + print( + f"Created missing key '{key}' in path '{list_key_path}' in: {relative_path}" + ) + current_level = current_level[key] + + with open(file_path, 'w') as f: + f.writelines(header_lines) + yaml.dump(data, f) + + except Exception as e: + print(f"Error processing YAML in {relative_path}: {e}") + + +def set_yaml_value(build_dir, files_patterns, key_path, value): + """Sets or replaces a specific key's value in the specified YAML files, preserving the header. + + This function finds all YAML files matching the given patterns and sets a + specified key to a new value. The key can be nested, using dot notation + for the path (e.g., 'parent.child.key'). + + Unlike `update_yaml_key`, this function will always set the value. If the + key or any part of its path does not exist, it will be created. This is + useful for overwriting existing values or ensuring a key is present with + a specific value. + + Args: + build_dir (str): The absolute path to the base directory where the + YAML files are located. + files_patterns (list[str]): A list of file path patterns to process, + relative to `build_dir`. Supports glob patterns. + key_path (str): The dot-separated path to the key to set or create + (e.g., 'collections', 'info.version'). + value (any): The new value to assign to the key. This can be any + YAML-compatible type (string, list, dictionary, etc.). + + Example: + ```yaml + changes: + - type: set_yaml_value + files: + - "special_actions/*/ansible_requirements.yml" + key: "collections" + value: [] + ``` + """ + + for file_pattern in files_patterns: + full_pattern = os.path.join(build_dir, file_pattern) + for file_path in glob.glob(full_pattern): + if os.path.exists(file_path): + relative_path = os.path.relpath(file_path, build_dir) + try: + header_lines = read_yaml_header(file_path) + with open(file_path, 'r') as f: + # Skip header lines when loading + for _ in header_lines: + next(f, None) + data = yaml.load(f) + + keys = key_path.split('.') + current_level = data + for i, key in enumerate(keys): + if i == len(keys) - 1: + current_level[key] = value + print(f"Set key '{key_path}' in: {relative_path}") + else: + if key not in current_level: + current_level[key] = {} + print( + f"Created missing key '{key}' in path '{key_path}' in: {relative_path}" + ) + current_level = current_level[key] + + with open(file_path, 'w') as f: + f.writelines(header_lines) + yaml.dump(data, f) + + except Exception as e: + print(f"Error processing YAML in {relative_path}: {e}") + + +def remove_yaml_list_items(build_dir, files_patterns, list_key_path, items_to_remove): + """Removes specific items from a list in the specified YAML files. + + This function finds all YAML files matching the given glob patterns and + removes specified items from a list within those files. The list is + identified by a dot-separated key path. + + Args: + build_dir (str): The absolute path to the base directory where the + YAML files are located. + files_patterns (list[str]): A list of file path patterns to process, + relative to `build_dir`. Supports glob patterns. + list_key_path (str): The dot-separated path to the list from which + items will be removed (e.g., 'collections'). + items_to_remove (list[any]): A list of specification items. Any item + in the target list that matches one of these specifications will + be removed. + + Example: + ```yaml + changes: + - type: remove_yaml_list_items + files: + - "deploy_scenarios/*/ansible_requirements.yml" + list_key: "collections" + items_to_remove: + - name: "community.sap_install" + ``` + """ + + def item_matches(list_item, spec_item): + """Check if a list item matches a specification item.""" + if isinstance(list_item, dict) and isinstance(spec_item, dict): + # For dicts, check if list_item contains all key-value pairs from spec_item. + return all(k in list_item and list_item[k] == v for k, v in spec_item.items()) + # For other types (like strings), use simple equality. + return list_item == spec_item + + for file_pattern in files_patterns: + full_pattern = os.path.join(build_dir, file_pattern) + for file_path in glob.glob(full_pattern): + if os.path.isfile(file_path): + relative_path = os.path.relpath(file_path, build_dir) + try: + header_lines = read_yaml_header(file_path) + with open(file_path, 'r') as f: + for _ in header_lines: + next(f, None) + data = yaml.load(f) + + # Skip if there is nothing to update. + if data is None: + continue + + keys = list_key_path.split('.') + current_level = data + modified = False + path_exists = True + + for i, key in enumerate(keys): + if not (isinstance(current_level, dict) and key in current_level): + path_exists = False + break + if i < len(keys) - 1: + current_level = current_level[key] + + if not path_exists: + print(f"Warning: Path '{list_key_path}' not found in {relative_path}. Skipping.") + continue + + list_key = keys[-1] + if isinstance(current_level[list_key], list): + target_list = current_level[list_key] + initial_len = len(target_list) + new_list = [item for item in target_list if not any(item_matches(item, rem_spec) for rem_spec in items_to_remove)] + if len(new_list) < initial_len: + current_level[list_key] = new_list + print(f"Removed {initial_len - len(new_list)} item(s) from list '{list_key_path}' in: {relative_path}") + modified = True + + if modified: + with open(file_path, 'w') as f: + f.writelines(header_lines) + yaml.dump(data, f) + except Exception as e: + print(f"Error processing YAML in {relative_path}: {e}") + + +def main(): + """Main function to load config and apply changes.""" + parser = argparse.ArgumentParser(description="Change files in a specified directory based on a configuration file.") + parser.add_argument("--build_dir", help="The path to the build directory where changes will occur." ) + parser.add_argument("--config", help="The path to the configuration YAML file (default: config.yaml)." ) + args = parser.parse_args() + + config_file = args.config + build_dir = args.build_dir + + if not os.path.isdir(build_dir): + print(f"Error: Build directory '{build_dir}' does not exist.") + return + + config = load_config(config_file) + changes = config.get('changes', []) + + for change in changes: + change_type = change.get('type') + if change_type == 'remove_paths': + remove_paths(build_dir, change.get('paths', [])) + elif change_type == 'remove_lines': + remove_lines(build_dir, change.get('files', []), change.get('patterns', [])) + elif change_type == 'replace_text': + replace_text(build_dir, change.get('files', []), change.get('replacements', [])) + elif change_type == 'update_yaml_key': + update_yaml_key(build_dir, change.get('files', []), change.get('key'), change.get('value')) + elif change_type == 'append_yaml_list': + append_yaml_list(build_dir, change.get('files', []), change.get('list_key'), change.get('new_item')) + elif change_type == 'set_yaml_value': + set_yaml_value(build_dir, change.get('files', []), change.get('key'), change.get('value')) + elif change_type == 'remove_yaml_list_items': + remove_yaml_list_items(build_dir, change.get('files', []), change.get('list_key'), change.get('items_to_remove')) + else: + print(f"Unknown changes type: {change['type']}") + + +if __name__ == "__main__": + main() -- 2.51.1 From 9f3a83bf735d824c5fcbda73c396e8ac315a1680cb927ff6a3f79bcf41056d56 Mon Sep 17 00:00:00 2001 From: Marcel Mamula Date: Mon, 12 Jan 2026 11:49:00 +0000 Subject: [PATCH 2/2] Release 1.3.1 rpmlintrc updated OBS-URL: https://build.opensuse.org/package/show/devel:sap:ansible/ansible-sap-launchpad?expand=0&rev=12 --- ansible-sap-launchpad.spec | 1 + 1 file changed, 1 insertion(+) diff --git a/ansible-sap-launchpad.spec b/ansible-sap-launchpad.spec index 3036bb0..c080bd8 100644 --- a/ansible-sap-launchpad.spec +++ b/ansible-sap-launchpad.spec @@ -27,6 +27,7 @@ URL: https://github.com/sap-linuxlab/community.sap_launchpad/ Source0: %{url}archive/refs/tags/%{version}.tar.gz#/%{name}-%{version}.tar.gz Source1: %{name}.yaml Source2: transformation.py +Source99: %{name}.rpmlintrc BuildArch: noarch -- 2.51.1