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/common.mk b/common.mk
index 77e029c6..b4d7d632 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)"
diff --git a/generate_version_table.py b/generate_version_table.py
new file mode 100755
index 00000000..5f075bae
--- /dev/null
+++ b/generate_version_table.py
@@ -0,0 +1,138 @@
+#!/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: 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[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 "
+ + 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()
+ 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() -> list[str]:
+ 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)
+ return []
+
+
+# generates the table string
+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
+ # 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: 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,
+ )
+ 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])
diff --git a/tests/check_generate_version_table.sh b/tests/check_generate_version_table.sh
new file mode 100755
index 00000000..be2af7ef
--- /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