Sync from SUSE:SLFO:Main ansible-sap-operations revision 0375477658de126dcf3d7332c4a01ccd
This commit is contained in:
BIN
ansible-sap-operations-0.9.1.tar.gz
(Stored with Git LFS)
BIN
ansible-sap-operations-0.9.1.tar.gz
(Stored with Git LFS)
Binary file not shown.
@@ -1,5 +1,11 @@
|
||||
-------------------------------------------------------------------
|
||||
Thu Apr 10 14:38:36 UTC 2025 - Marcel Mamula <marcel.mamula@suse.com>
|
||||
|
||||
- Updated downstream transformation
|
||||
- Removed python dependencies and macros from spec file
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Jan 20 09:00:00 UTC 2025 - Marcel Mamula <marcel.mamula@suse.com>
|
||||
|
||||
- 0.9.1
|
||||
- Initial release.
|
||||
- Initial release.
|
||||
|
@@ -16,7 +16,6 @@
|
||||
#
|
||||
|
||||
%define ansible_collection_name sap_operations
|
||||
%define ansible_roles_to_remove "sap_rhsm sap_fapolicy"
|
||||
%define ansible_collection_path %{_datadir}/ansible/collections/ansible_collections/suse/%{ansible_collection_name}
|
||||
|
||||
|
||||
@@ -26,37 +25,20 @@ License: Apache-2.0
|
||||
Version: 0.9.1
|
||||
Release: 0
|
||||
URL: https://github.com/SUSE/community.sap_operations/
|
||||
Source: %{url}archive/refs/tags/%{version}-suse.tar.gz#/%{name}-%{version}.tar.gz
|
||||
|
||||
Source0: %{url}archive/refs/tags/%{version}.tar.gz#/%{name}-%{version}.tar.gz
|
||||
Source1: ansible-sap-operations.yaml
|
||||
Source2: collection_update.py
|
||||
|
||||
BuildArch: noarch
|
||||
|
||||
|
||||
# Python macros are required for python detection
|
||||
BuildRequires: python-rpm-macros
|
||||
Requires: ansible-core >= 2.16
|
||||
Requires: ansible >= 9
|
||||
BuildRequires: ansible-core >= 2.16
|
||||
BuildRequires: ansible >= 9
|
||||
|
||||
# Minimum python version
|
||||
%{?sle15_python_module_pythons}
|
||||
BuildRequires: %{python_module base >= 3.11}
|
||||
Requires: %{python_module base >= 3.11}
|
||||
|
||||
|
||||
# Select correct supported Ansible
|
||||
%if 0%{?suse_version} >= 1600
|
||||
Requires: ansible-core
|
||||
Requires: ansible
|
||||
BuildRequires: ansible-core
|
||||
BuildRequires: ansible
|
||||
%else
|
||||
# Only Ansible 9 is supported on SLES 15
|
||||
Requires: ansible-core-2.16
|
||||
Requires: ansible-9
|
||||
BuildRequires: ansible-core-2.16
|
||||
BuildRequires: ansible-9
|
||||
%endif
|
||||
|
||||
# Do not check any files in collections for requires
|
||||
%global __requires_exclude_from ^%{python311_sitelib}/.*$
|
||||
# Python module ruamel.yaml for collection-update.py
|
||||
BuildRequires: python3-ruamel.yaml
|
||||
|
||||
|
||||
%description
|
||||
@@ -73,14 +55,12 @@ update SAP profiles, configure firewall rules, execute RFC, etc.
|
||||
cd %{_builddir}
|
||||
tar -xzf %{_sourcedir}/%{name}-%{version}.tar.gz --strip-components=1
|
||||
|
||||
# Execute python script to update documentation and remove unsupported roles
|
||||
python3 %{_sourcedir}/collection_update.py --config %{_sourcedir}/%{name}.yaml --build_dir %{_builddir}
|
||||
|
||||
|
||||
%build
|
||||
cd %{_builddir}
|
||||
# Remove unsupported roles before building collection
|
||||
for role in $(echo %{ansible_roles_to_remove}); do
|
||||
rm -rf %{_builddir}/roles/$role
|
||||
done
|
||||
|
||||
# Build the Ansible collection
|
||||
ansible-galaxy collection build --output-path %{_builddir}
|
||||
|
||||
|
||||
|
65
ansible-sap-operations.yaml
Normal file
65
ansible-sap-operations.yaml
Normal file
@@ -0,0 +1,65 @@
|
||||
# Configuration file for collection changes before build.
|
||||
# Collection: community.sap_operations
|
||||
|
||||
# Types of changes used by collection_update.py
|
||||
# remove_paths - Remove specific directories and files that are no longer needed.
|
||||
# remove_lines - Remove specific lines containing certain patterns from files.
|
||||
# replace_text - Replace specific text strings within files.
|
||||
# update_yaml_key - Update specific keys in YAML files with new values.
|
||||
# append_yaml_list - Append items to a list in a YAML file.
|
||||
|
||||
changes:
|
||||
# Main collection changes
|
||||
|
||||
- type: remove_paths
|
||||
# Remove all unsupported roles.
|
||||
# Remove all 'meta/main.yml' files within any role directory.
|
||||
paths:
|
||||
- "roles/sap_rhsm"
|
||||
- "roles/sap_fapolicy"
|
||||
- "roles/*/meta/main.yml"
|
||||
|
||||
- type: remove_lines
|
||||
# Remove specific lines from README.md and all role README.md files.
|
||||
# This is used to clean up any mention of unsupported roles and leftover Ansible Lint markers.
|
||||
files:
|
||||
- "README.md"
|
||||
- "roles/*/README.md"
|
||||
patterns:
|
||||
- "sap_rhsm"
|
||||
- "sap_fapolicy"
|
||||
- "\\[Ansible Lint"
|
||||
|
||||
- type: replace_text
|
||||
# Replace specific text strings in galaxy.yml and README.md files.
|
||||
# This is used to update the namespace and repository links from 'community' to 'suse'.
|
||||
files:
|
||||
- "galaxy.yml"
|
||||
- "README.md"
|
||||
replacements:
|
||||
- { find: "namespace: community", replace: "namespace: suse" }
|
||||
- { find: "github.com/sap-linuxlab/community", replace: "github.com/SUSE/community" }
|
||||
- { find: "documentation:.*", replace: "documentation: https://github.com/SUSE/community.sap_operations/blob/main/README.md" }
|
||||
|
||||
- type: update_yaml_key
|
||||
# Update the 'authors' key in galaxy.yml to 'SUSE'.
|
||||
files:
|
||||
- "galaxy.yml"
|
||||
key: "authors"
|
||||
value:
|
||||
- SUSE
|
||||
|
||||
- type: update_yaml_key
|
||||
# Update the 'build_ignore' key in galaxy.yml to ignore specific files and directories during the build process.
|
||||
# This is used to exclude test files, git directories, linting configuration files, and workflows.
|
||||
files:
|
||||
- "galaxy.yml"
|
||||
key: "build_ignore"
|
||||
value:
|
||||
- "tests"
|
||||
- ".git*"
|
||||
- ".ansible-lint"
|
||||
- ".yamllint*"
|
||||
- ".pylintrc*"
|
||||
- "bindep*"
|
||||
- ".pre-commit-config.yaml"
|
263
collection_update.py
Normal file
263
collection_update.py
Normal file
@@ -0,0 +1,263 @@
|
||||
# This Python script updates downstream collection details before execution of ansible-galaxy build.
|
||||
# Intended use: Executed by RPM 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 within the build directory.
|
||||
|
||||
Args:
|
||||
build_dir (str): The base directory where the files/directories are located.
|
||||
paths (list): A list of path patterns (relative to build_dir) to remove.
|
||||
These can be file paths or directory paths.
|
||||
Supports glob patterns.
|
||||
"""
|
||||
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 specified files within the build directory.
|
||||
|
||||
Args:
|
||||
build_dir (str): The base directory where the files are located.
|
||||
files_patterns (list): A list of file path patterns (relative to build_dir) to modify.
|
||||
Supports glob patterns.
|
||||
patterns (list): A list of regular expression patterns.
|
||||
Lines matching any of these patterns will be removed.
|
||||
"""
|
||||
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 the specified files within the build directory.
|
||||
|
||||
Args:
|
||||
build_dir (str): The base directory where the files are located.
|
||||
files_patterns (list): A list of file path patterns (relative to build_dir) to modify.
|
||||
Supports glob patterns.
|
||||
replacements (list): A list of dictionaries, each with 'find' and 'replace' keys.
|
||||
'find' is the regular expression pattern to search for.
|
||||
'replace' is the text to replace it with.
|
||||
"""
|
||||
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, key_path, value):
|
||||
"""Updates a specific key's value in the specified YAML files, preserving the header.
|
||||
|
||||
Args:
|
||||
build_dir (str): The base directory where the YAML files are located.
|
||||
files (list): A list of file paths (relative to build_dir) to modify.
|
||||
key_path (str): The path to the key to update, using dot notation (e.g., 'a.b.c').
|
||||
value (any): The new value to set for the specified key.
|
||||
"""
|
||||
for file_path in files:
|
||||
full_path = os.path.join(build_dir, file_path)
|
||||
if os.path.exists(full_path):
|
||||
try:
|
||||
relative_path = os.path.relpath(full_path, build_dir)
|
||||
header_lines = read_yaml_header(full_path)
|
||||
with open(full_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(full_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, list_key_path, new_item):
|
||||
"""Appends a new item to a list in the specified YAML files, preserving the header.
|
||||
|
||||
Args:
|
||||
build_dir (str): The base directory where the YAML files are located.
|
||||
files (list): A list of file paths (relative to build_dir) to modify.
|
||||
list_key_path (str): The path to the list to append to, using dot notation (e.g., 'a.b.c').
|
||||
new_item (any): The new item to append to the list.
|
||||
"""
|
||||
for file_path in files:
|
||||
full_path = os.path.join(build_dir, file_path)
|
||||
if os.path.exists(full_path):
|
||||
try:
|
||||
relative_path = os.path.relpath(full_path, build_dir)
|
||||
header_lines = read_yaml_header(full_path)
|
||||
with open(full_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(full_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'))
|
||||
else:
|
||||
print(f"Unknown changes type: {change['type']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Reference in New Issue
Block a user