From 1ddafba3826585cd2fde3fd9126fd8f744540f4d Mon Sep 17 00:00:00 2001 From: Petr Khartskhaev Date: Wed, 29 Oct 2025 11:52:45 +0100 Subject: [PATCH 1/5] Add script to generate table of supported distros across different versions of the software, along with a makefile entry for it --- common.mk | 3 ++ generate_version_table.py | 105 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 generate_version_table.py diff --git a/common.mk b/common.mk index 77e029c6..9e6d2ae0 100644 --- a/common.mk +++ b/common.mk @@ -158,3 +158,6 @@ generate: $(generator) -v $$version -m manifest.yml -s specs/multispec.yml ; \ fi \ done + +version-table: + $(common_dir)/generate_version_table.py "$(BASE_IMAGE_NAME)" \ No newline at end of file diff --git a/generate_version_table.py b/generate_version_table.py new file mode 100644 index 00000000..71fdd4b0 --- /dev/null +++ b/generate_version_table.py @@ -0,0 +1,105 @@ +#!/usr/bin/python3 +import os +import re +import sys +from natsort import natsorted + +distro_names = { + "c9s": ["CentOS Stream 9", "quay.io/sclorg/%s-c9s"], + "c10s": ["CentOS Stream 10", "quay.io/sclorg/%s-c10s"], + "fedora": ["Fedora", "quay.io/fedora/%s"], + "rhel8": ["RHEL 8", "registry.redhat.io/rhel8/%s"], + "rhel9": ["RHEL 9", "registry.redhat.io/rhel9/%s"], + "rhel10": ["RHEL 10", "registry.redhat.io/rhel10/%s"] +} +version_regex = re.compile(r"^VERSIONS\s*=\s*(.*)$") +docker_file_regex = re.compile(r"(?<=Dockerfile\.).+") +exclude_file_regex = re.compile(r"(?<=\.exclude-).+") + +table_regex = re.compile(r"(\n).*?(\n)", re.DOTALL) + +def main(name): + docker_distros = {} + all_distros = set() + + versions = _get_versions() + + # goes through all the versions and gets their dockerfile + # and exclude- distros + for version in versions: + files = "\n".join(os.listdir(version)) + available_distros = set(re.findall(docker_file_regex, files)) + exclude_distros = set(re.findall(exclude_file_regex, files)) + unsupported = available_distros - distro_names.keys() + if len(unsupported) > 0: + print(f"WARNING: Distros {list(unsupported)} in version {version} are unsupported and Dockerfiles for them should be deleted") + all_distros |= available_distros + docker_distros[version] = (available_distros - exclude_distros) & distro_names.keys() + all_distros &= distro_names.keys() + + table = _create_table(natsorted(all_distros), versions, docker_distros, name) + _replace_in_readme(table) + +# gets the versions of the container from the Makefile +def _get_versions(): + try: + with open('Makefile', 'r') as f: + for line in f: + match = re.search(version_regex, line) + if match: + return match.group(1).split(" ") + except Exception as e: + print(f"An exception occurred when trying to read the Makefile: {e}", file=sys.stderr) + exit(1) + + print(f"No VERSIONS variable found in Makefile, please make sure the syntax is correct") + exit(2) + +# generates the table string +def _create_table(distros, versions, docker_distros, name): + # table header + table = f"||{'|'.join([distro_names[distro][0] for distro in distros])}|\n" + # prints the table column separator + # align the versions to left and ticks to center + table += f"|:--|{':--:|' * len(distros)}\n" + for version in versions: + # prints the version line header + table += f"|{version}" + # goes over the distros and prints a tick and repo address + # if the image is available + for distro in distros: + table += '|' + if distro in docker_distros[version]: + table += f"
`{distro_names[distro][1] % (name + "-" + version.replace('.', ''))}`
" + # end the table line + table += '|\n' + return table + +# reads the README.md, finds the Table start and Table end comments +# replaces any string between them with the table string +# and writes it back to the README.md file +def _replace_in_readme(table): + try: + with open("README.md", "r+") as readme: + original_readme = readme.read() + new_readme, subs = re.subn(table_regex, f"\\1{table}\\2", original_readme) + if subs == 0: + print("The Table start and Table end tag not found, not modifying README.md", file=sys.stderr) + exit(0) + if subs > 1: + print("More than one Table start and Table end tag found, not modifying README.md", file=sys.stderr) + exit(0) + readme.seek(0) + readme.write(new_readme) + readme.truncate() + except Exception as e: + print(f"An error occurred while trying to open README.md: {e}", file=sys.stderr) + exit(1) + +if __name__ == "__main__": + args = sys.argv[1:] + if len(args) != 1: + print("Usage: ./generate_table.py NAME\nThe NAME of the image is required") + exit(2) + main(args[0]) + From 3cce51308bc43ee1f868cfc483c9769a1a543205 Mon Sep 17 00:00:00 2001 From: Petr Khartskhaev Date: Wed, 29 Oct 2025 12:07:08 +0100 Subject: [PATCH 2/5] Make the python script executable --- generate_version_table.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 generate_version_table.py diff --git a/generate_version_table.py b/generate_version_table.py old mode 100644 new mode 100755 From db01b7c8ecab693933a5b8fbe6fcbf3f9684bceb Mon Sep 17 00:00:00 2001 From: Petr Khartskhaev Date: Thu, 30 Oct 2025 13:24:13 +0100 Subject: [PATCH 3/5] Added a test for version table generation script --- generate_version_table.py | 14 ++- tests/check_generate_version_table.sh | 118 ++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 4 deletions(-) create mode 100755 tests/check_generate_version_table.sh diff --git a/generate_version_table.py b/generate_version_table.py index 71fdd4b0..5140aa7a 100755 --- a/generate_version_table.py +++ b/generate_version_table.py @@ -18,6 +18,7 @@ table_regex = re.compile(r"(\n).*?(\n)", re.DOTALL) + def main(name): docker_distros = {} all_distros = set() @@ -25,14 +26,16 @@ def main(name): versions = _get_versions() # goes through all the versions and gets their dockerfile - # and exclude- distros + # and 'exclude-' distros for version in versions: files = "\n".join(os.listdir(version)) available_distros = set(re.findall(docker_file_regex, files)) exclude_distros = set(re.findall(exclude_file_regex, files)) unsupported = available_distros - distro_names.keys() if len(unsupported) > 0: - print(f"WARNING: Distros {list(unsupported)} in version {version} are unsupported and Dockerfiles for them should be deleted") + print( + f"WARNING: Distros {list(unsupported)} in version {version} are unsupported and Dockerfiles for them should be deleted", + file=sys.stderr) all_distros |= available_distros docker_distros[version] = (available_distros - exclude_distros) & distro_names.keys() all_distros &= distro_names.keys() @@ -40,6 +43,7 @@ def main(name): table = _create_table(natsorted(all_distros), versions, docker_distros, name) _replace_in_readme(table) + # gets the versions of the container from the Makefile def _get_versions(): try: @@ -52,9 +56,10 @@ def _get_versions(): print(f"An exception occurred when trying to read the Makefile: {e}", file=sys.stderr) exit(1) - print(f"No VERSIONS variable found in Makefile, please make sure the syntax is correct") + print(f"No VERSIONS variable found in Makefile, please make sure the syntax is correct", file=sys.stderr) exit(2) + # generates the table string def _create_table(distros, versions, docker_distros, name): # table header @@ -75,6 +80,7 @@ def _create_table(distros, versions, docker_distros, name): table += '|\n' return table + # reads the README.md, finds the Table start and Table end comments # replaces any string between them with the table string # and writes it back to the README.md file @@ -96,10 +102,10 @@ def _replace_in_readme(table): print(f"An error occurred while trying to open README.md: {e}", file=sys.stderr) exit(1) + if __name__ == "__main__": args = sys.argv[1:] if len(args) != 1: print("Usage: ./generate_table.py NAME\nThe NAME of the image is required") exit(2) main(args[0]) - diff --git a/tests/check_generate_version_table.sh b/tests/check_generate_version_table.sh new file mode 100755 index 00000000..1e2174c7 --- /dev/null +++ b/tests/check_generate_version_table.sh @@ -0,0 +1,118 @@ +#!/usr/bin/bash + +if [ "$(dirname "$0")" != "." ]; then + echo "You need to run this script from the directory it's located in (./tests)" + exit 1 +fi + +# setup +mkdir test-container || exit 1 +pushd test-container || exit 1 +echo " +BASE_IMAGE_NAME = test +VERSIONS = 1.2 2.3 +include ../../common.mk +" > Makefile +export common_dir="../.." + +# create base files for the script to work +mkdir 1.2 +mkdir 2.3 +touch 1.2/Dockerfile.c8s +touch 1.2/Dockerfile.c9s +touch 1.2/Dockerfile.fedora +touch 1.2/Dockerfile.rhel8 +touch 1.2/Dockerfile.rhel9 +touch 1.2/.exclude-c9s +touch 1.2/.exclude-rhel9 +touch 2.3/Dockerfile.c9s +touch 2.3/Dockerfile.c10s +touch 2.3/Dockerfile.fedora +touch 2.3/Dockerfile.rhel9 +touch 2.3/Dockerfile.rhel10 +touch 2.3/.exclude-fedora + +# test README without table tags +touch README.md +make version-table &>/dev/null || exit 1 +if [ ! -s "README.md" ]; then + echo "[PASS] README without table tags not modified" +else + echo "[FAIL] README without table tags modified" +fi + +# test README with table tags +echo " + +this will be overwritten + +text outside +" > README.md + +echo " + +||CentOS Stream 9|CentOS Stream 10|Fedora|RHEL 8|RHEL 9|RHEL 10| +|:--|:--:|:--:|:--:|:--:|:--:|:--:| +|1.2|||
\`quay.io/fedora/test-12\`
|
\`registry.redhat.io/rhel8/test-12\`
||| +|2.3|
\`quay.io/sclorg/test-23-c9s\`
|
\`quay.io/sclorg/test-23-c10s\`
|||
\`registry.redhat.io/rhel9/test-23\`
|
\`registry.redhat.io/rhel10/test-23\`
| + +text outside +" > README.expected +make version-table &>/dev/null || exit 1 +if diff README.md README.expected ; then + echo "[PASS] README with table tags modified correctly" +else + echo "[FAIL] README with table tags modified incorrectly or not modified" +fi + +# test README with multiple pairs of table tags +echo " + + +text inbetween + + +" > README.md + +echo " + + +text inbetween + + +" > README.expected + +make version-table &>/dev/null || exit 1 +if diff README.md README.expected ; then + echo "[PASS] README with multiple pairs of table tags unmodified" +else + echo "[FAIL] README with multiple pairs of table tags modified" +fi + +# cleanup +popd || exit 1 +rm -rf test-container \ No newline at end of file From c43e5ae9670875830027ffb805c55f2e2a677303 Mon Sep 17 00:00:00 2001 From: Petr Khartskhaev Date: Fri, 31 Oct 2025 15:04:10 +0100 Subject: [PATCH 4/5] Bump version for `black` in `.pre-commit-config`, fixed formatting and type signatures, and changed error handling for `_get_versions()` --- .pre-commit-config.yaml | 2 +- generate_version_table.py | 67 +++++++++++++++++++++++++++------------ 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ed3de8e..83b6a803 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ repos: - repo: https://github.com/ambv/black - rev: 22.6.0 + rev: 25.9.0 hooks: - id: black - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/generate_version_table.py b/generate_version_table.py index 5140aa7a..5f075bae 100755 --- a/generate_version_table.py +++ b/generate_version_table.py @@ -10,34 +10,46 @@ "fedora": ["Fedora", "quay.io/fedora/%s"], "rhel8": ["RHEL 8", "registry.redhat.io/rhel8/%s"], "rhel9": ["RHEL 9", "registry.redhat.io/rhel9/%s"], - "rhel10": ["RHEL 10", "registry.redhat.io/rhel10/%s"] + "rhel10": ["RHEL 10", "registry.redhat.io/rhel10/%s"], } version_regex = re.compile(r"^VERSIONS\s*=\s*(.*)$") docker_file_regex = re.compile(r"(?<=Dockerfile\.).+") exclude_file_regex = re.compile(r"(?<=\.exclude-).+") -table_regex = re.compile(r"(\n).*?(\n)", re.DOTALL) +table_regex = re.compile( + r"(\n).*?(\n)", re.DOTALL +) -def main(name): +def main(name: str) -> None: docker_distros = {} all_distros = set() versions = _get_versions() + if len(versions) == 0: + print( + "No VERSIONS variable found in Makefile, please make sure the syntax is correct", + file=sys.stderr, + ) + exit(2) # goes through all the versions and gets their dockerfile # and 'exclude-' distros for version in versions: files = "\n".join(os.listdir(version)) - available_distros = set(re.findall(docker_file_regex, files)) + available_distros: set[str] = set(re.findall(docker_file_regex, files)) exclude_distros = set(re.findall(exclude_file_regex, files)) unsupported = available_distros - distro_names.keys() if len(unsupported) > 0: print( - f"WARNING: Distros {list(unsupported)} in version {version} are unsupported and Dockerfiles for them should be deleted", - file=sys.stderr) + f"WARNING: Distros {list(unsupported)} in version " + + f"{version} are unsupported and Dockerfiles for them should be deleted", + file=sys.stderr, + ) all_distros |= available_distros - docker_distros[version] = (available_distros - exclude_distros) & distro_names.keys() + docker_distros[version] = ( + available_distros - exclude_distros + ) & distro_names.keys() all_distros &= distro_names.keys() table = _create_table(natsorted(all_distros), versions, docker_distros, name) @@ -45,23 +57,29 @@ def main(name): # gets the versions of the container from the Makefile -def _get_versions(): +def _get_versions() -> list[str]: try: - with open('Makefile', 'r') as f: + with open("Makefile", "r") as f: for line in f: match = re.search(version_regex, line) if match: return match.group(1).split(" ") except Exception as e: - print(f"An exception occurred when trying to read the Makefile: {e}", file=sys.stderr) + print( + f"An exception occurred when trying to read the Makefile: {e}", + file=sys.stderr, + ) exit(1) - - print(f"No VERSIONS variable found in Makefile, please make sure the syntax is correct", file=sys.stderr) - exit(2) + return [] # generates the table string -def _create_table(distros, versions, docker_distros, name): +def _create_table( + distros: list[str], + versions: list[str], + docker_distros: dict[str, set[str]], + name: str, +) -> str: # table header table = f"||{'|'.join([distro_names[distro][0] for distro in distros])}|\n" # prints the table column separator @@ -73,27 +91,36 @@ def _create_table(distros, versions, docker_distros, name): # goes over the distros and prints a tick and repo address # if the image is available for distro in distros: - table += '|' + table += "|" if distro in docker_distros[version]: - table += f"
`{distro_names[distro][1] % (name + "-" + version.replace('.', ''))}`
" + table += ( + "
" + + f"`{distro_names[distro][1] % (name + "-" + version.replace('.', ''))}`
" + ) # end the table line - table += '|\n' + table += "|\n" return table # reads the README.md, finds the Table start and Table end comments # replaces any string between them with the table string # and writes it back to the README.md file -def _replace_in_readme(table): +def _replace_in_readme(table: str) -> None: try: with open("README.md", "r+") as readme: original_readme = readme.read() new_readme, subs = re.subn(table_regex, f"\\1{table}\\2", original_readme) if subs == 0: - print("The Table start and Table end tag not found, not modifying README.md", file=sys.stderr) + print( + "The Table start and Table end tag not found, not modifying README.md", + file=sys.stderr, + ) exit(0) if subs > 1: - print("More than one Table start and Table end tag found, not modifying README.md", file=sys.stderr) + print( + "More than one Table start and Table end tag found, not modifying README.md", + file=sys.stderr, + ) exit(0) readme.seek(0) readme.write(new_readme) From ee2dce3f30c9433de906b00896c4232d927b899f Mon Sep 17 00:00:00 2001 From: Petr Khartskhaev Date: Mon, 3 Nov 2025 16:17:51 +0100 Subject: [PATCH 5/5] fix ends of files --- common.mk | 2 +- tests/check_generate_version_table.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common.mk b/common.mk index 9e6d2ae0..b4d7d632 100644 --- a/common.mk +++ b/common.mk @@ -160,4 +160,4 @@ generate: done version-table: - $(common_dir)/generate_version_table.py "$(BASE_IMAGE_NAME)" \ No newline at end of file + $(common_dir)/generate_version_table.py "$(BASE_IMAGE_NAME)" diff --git a/tests/check_generate_version_table.sh b/tests/check_generate_version_table.sh index 1e2174c7..be2af7ef 100755 --- a/tests/check_generate_version_table.sh +++ b/tests/check_generate_version_table.sh @@ -115,4 +115,4 @@ fi # cleanup popd || exit 1 -rm -rf test-container \ No newline at end of file +rm -rf test-container