# The repository and source-tarball name, which is what the archive unpacks into. The same as the # package name today, and kept as its own global so that a repository rename is one line here rather # than an edit to the URL, the Source, and %%autosetup. %global upstream_name %{name} # The clpeak release bundled for the GPU benchmark. Pinned rather than tracked: a benchmark whose # result depends on what a mirror served that day is not comparable between machines, and nothing in # this suite downloads at run time. %global clpeak_version 2.0.19 # The interpreter this package runs on, per release. # # The rule is one interpreter per release that is supported for the remainder of that release's own # lifecycle. A certification run has to be reproducible years after it was submitted, so an # interpreter that goes end of life before the OS does is not a candidate however new it is. # # el8 python3.12, from AppStream. An ordinary package there rather than a module stream, so it # needs no module enablement. The platform python is 3.6, which is end of life and below this # suite's language floor, so it is not usable at all. # el9 python3, which is 3.9. That is the language floor exactly, and it is the system interpreter, # supported for as long as el9 is. Deliberately not an AppStream 3.12: it would be a second # Python on the machine for no gain, and 3.9 is the floor precisely because el9 is where the # suite has to run on it. # el10 python3, which is 3.12 and the system interpreter there. # # So 3.12 on 8 and 10, 3.9 on 9, and the floor is load-bearing on el9 alone. The %%check below runs # every module through whichever of these it is, which is what turns that floor from a claim in # pyproject.toml into something a build fails over. curses comes with each of these (python3.12-libs # on el8, python3-libs on 9 and 10) and the guided interface needs it. %if 0%{?rhel} == 8 %global almacert_python_pkg python3.12 %else %global almacert_python_pkg python3 %endif # The interpreter's binary has the same name as its package on every release above, so derive it # rather than writing the version twice and letting the two drift. %global almacert_python %{_bindir}/%{almacert_python_pkg} # Where the suite itself lives. Named once because it appears in %%install, %%check, the launcher, and # %%files, and a path repeated five times is a path that gets changed in four of them. %global almacert_home %{_datadir}/%{name} # This package's Python is byte-compiled by hand in %%install, with the interpreter above, so the # automatic pass must not also compile it with a different one: on el8 that would be python3.6 # against 3.9+ syntax. Nothing else here is Python, so turning the automatic pass off entirely is # both safe and the only way to keep the two from disagreeing. %global _python_bytecompile_extra 0 %global __brp_python_bytecompile %{nil} Name: alma-cert Version: 0.1.0 Release: 16%{?dist} Summary: AlmaLinux hardware certification suite License: MIT URL: https://github.com/AlmaLinux/%{upstream_name} # Plain URLs, spelled out. GitHub serves an arbitrary final path component as the download filename, # which is what gives these meaningful names with no macro package involved. # # Building Source0 from a working tree rather than a tag: see the packaging section of # docs/catalog.md. It has the tar invocation and, more usefully, why the arguments are the way they # are. Not repeated here, because rpm reads a trailing backslash in a comment as a line continuation # and expands macros inside one, so a copy-pasteable command in this file is a spec that fails to # parse. Source0: %{url}/archive/v%{version}/%{upstream_name}-%{version}.tar.xz Source1: https://github.com/krrishnarraj/clpeak/archive/%{clpeak_version}/clpeak-%{clpeak_version}.tar.gz BuildArch: noarch BuildRequires: %{almacert_python_pkg} BuildRequires: python3-rpm-macros Requires: %{almacert_python_pkg} %if 0%{?rhel} != 8 Requires: python3 >= 3.9 %endif # Result bundles are .tar.zst, and the stdlib-only runtime compresses through this command. Requires: zstd # clpeak's source ships inside this package and is extracted and built on the machine under test, so # it is bundled in the sense the guidelines mean and is declared as such. Provides: bundled(clpeak) = %{clpeak_version} # used by collection stage Requires: dmidecode Requires: pciutils Requires: usbutils Requires: util-linux Requires: ethtool Requires: smartmontools Requires: nvme-cli # The guided interface reads terminfo. Ordinary terminals are covered by ncurses-base, which comes # with the interpreter's curses module; ncurses-term carries the entries a mainframe console needs, # so an s390x install can find ibm327x instead of falling back to no color at all. Recommends: ncurses-term %description alma-cert runs three kinds of certification runs on AlmaLinux systems: hardware inventory collection, functional validation tests, and benchmarks. Results are written as a versioned JSON report plus raw artifacts, bundled into a tarball that can be submitted to the AlmaLinux certification web application (Lumina) online or uploaded manually offline. %prep %autosetup -n %{upstream_name}-%{version} # Source1 is the only supplier of the clpeak tarball. A working tree that has had one fetched into it # carries it along in the source archive, and then %%install added Source1 beside it and the package # shipped two: the payload held 2.0.18 and 2.0.19 while Provides said 2.0.19. Found by bumping # %%clpeak_version and looking at the payload rather than at the macro. rm -f almacert/data/clpeak-*.tar.gz %build %install install -d %{buildroot}%{almacert_home} cp -a almacert %{buildroot}%{almacert_home}/ # Which package build produced a report, so a stored result can be traced back to the exact suite # that measured it. Written into the buildroot rather than the source tree for the reason above. echo "%{version}-%{release}" > %{buildroot}%{almacert_home}/almacert/data/BUILDINFO # The pinned clpeak source, under the name the runtime globs for. install -p -m 0644 %{SOURCE1} \ %{buildroot}%{almacert_home}/almacert/data/clpeak-%{clpeak_version}.tar.gz # Private to this application rather than installed into %%{python3_sitelib} by the pyproject macros, # which are otherwise the right way to package Python and would replace this whole section, the # launcher below, and the byte-compile globals at the top. # # **AlmaLinux 9 is the reason, and it is the only one.** Measured, not assumed: # # el10 %%pyproject_wheel against python3 (3.12) builds and installs cleanly. # el8 the same against python3.12 builds and installs cleanly, with BuildRequires written out by # hand because rpm 4.14 has no dynamic ones - ``rpmbuild -br`` is an unknown option there. # el9 cannot. This project's pyproject.toml is PEP 621, so building it needs setuptools 61 or # newer to read the [project] table, and the only thing on el9 providing # python3dist(setuptools) is 53.0.0 in BaseOS. # # python3.12-setuptools 68 is in el9 AppStream, and pointing the macros at it is not the way # out: el9 runs 3.9 by policy, for the reasons at the top of this file, so building against a # 3.12 stack there would package the suite for an interpreter it is not meant to use on that # release. el9 also has no python3.12-rpm-macros to do it with, but the policy is the reason. # # So the choice was one install layout everywhere, or the macros on 8 and 10 and something else on 9. # For a tool whose job is to produce comparable results, "where is it installed" changing per release # is the wrong thing to be clever about: it changes what a bug report means. # # The one thing that would undo this is setuptools 61 or newer for el9's python3.9, which would let # every release build the same way and delete this section, the launcher below, and the byte-compile # globals at the top. Nothing else: not a different interpreter on el9, and not a setup.cfg shim, # which would move this project's packaging metadata back to a legacy format to suit one distribution # and leave pyproject.toml decorative. %py_byte_compile %{almacert_python} %{buildroot}%{almacert_home}/almacert install -d %{buildroot}%{_bindir} # A launcher rather than a console script, because this package's interpreter is not the default # python3 on every release it supports. # # -I isolates it: no working directory on sys.path, no PYTHONPATH, no user site. That matters for a # tool run as root whose imports have to be its own. It replaces -P, which does only the first of # those three and does not exist before Python 3.11, so the command exited with a usage message on # el9, whose python3 is 3.9. cat > %{buildroot}%{_bindir}/%{name} <&2 exit 1 } done # Not the unit tests: pytest is not packaged for el8's python3.12, and a build must not fetch it. # What this does catch is the failure that matters when the interpreter or the language floor moves, # which is a module that will not import on the interpreter this package ships against. %{almacert_python} -I -c 'import importlib, pkgutil, sys; \ sys.path.insert(0, "%{buildroot}%{almacert_home}"); \ import almacert; \ [importlib.import_module(module.name) \ for module in pkgutil.walk_packages(almacert.__path__, "almacert.")]' # The launcher is the one file here with no unit test behind it. It names its final install path, # which does not exist yet during a build, so what can be checked is that it parses as a shell script # and that the command line it wraps works when pointed at the buildroot. Everything except the # literal path, which only an installed package can exercise. sh -n %{buildroot}%{_bindir}/%{name} grep -q -- ' -I -c ' %{buildroot}%{_bindir}/%{name} %{almacert_python} -I -c 'import sys; \ sys.path.insert(0, "%{buildroot}%{almacert_home}"); \ from almacert.cli import main; \ sys.exit(main(["--help"]))' >/dev/null %files %license LICENSE %doc README.md docs/ %{_bindir}/%{name} %{_mandir}/man1/%{name}.1* # rpmlint reports the .c and .cu files under here as devel-file-in-non-devel-package. They are not # development files: they are probe sources the suite compiles on the machine under test, which is # what lets it measure a GPU or a memory latency without downloading anything at run time. There is # no -devel subpackage to move them to and moving them would break the tests that build them. %{almacert_home}/ %config(noreplace) %{_sysconfdir}/%{name}/%{name}.conf # 0750, which rpmlint reports as non-standard-dir-perm. Deliberate: a run directory holds a full # hardware inventory, including serial numbers and DMI UUIDs, and an unsubmitted report of a machine # somebody has not announced yet. Nothing but this tool, which runs as root, has any reason to read # it, so it is not world-readable. %dir %attr(0750,root,root) %{_sharedstatedir}/%{name} %dir %attr(0750,root,root) %{_sharedstatedir}/%{name}/runs %changelog * Fri Aug 21 2026 Jonathan Wright - 0.1.0-16 - rebuilt * Fri Aug 21 2026 Jonathan Wright - 0.1.0-15 - rebuilt * Thu Aug 20 2026 Jonathan Wright - 0.1.0-14 - rebuilt * Thu Aug 20 2026 Jonathan Wright - 0.1.0-13 - rebuilt * Thu Aug 20 2026 Jonathan Wright - 0.1.0-12 - rebuilt * Thu Aug 20 2026 Jonathan Wright - 0.1.0-11 - rebuilt * Thu Aug 20 2026 Jonathan Wright - 0.1.0-10 - rebuilt * Thu Aug 20 2026 Jonathan Wright - 0.1.0-9 - rebuilt * Wed Aug 19 2026 Jonathan Wright - 0.1.0-8 - rebuilt * Wed Aug 19 2026 Jonathan Wright - 0.1.0-7 - rebuilt * Tue Aug 18 2026 Jonathan Wright - 0.1.0-6 - Fix the launcher on AlmaLinux 9: -P needs Python 3.11, so the command exited with a usage message on a 3.9 interpreter. Use -I, which every supported release has and which isolates PYTHONPATH and user site as well - Ship the pinned clpeak source as its own Source instead of expecting the build tree to carry it, and declare Provides: bundled(clpeak) - Byte-compile against the interpreter the package runs on, and keep the automatic pass from compiling 3.9+ syntax with el8's python3.6 - Plain full source URLs, drop the unused make build dependency, add %%check, and recommend ncurses-term for mainframe consoles - Ship a man page * Tue Aug 18 2026 Jonathan Wright - 0.1.0-5 - update * Tue Aug 18 2026 Jonathan Wright - 0.1.0-4 - update * Tue Aug 18 2026 Jonathan Wright - 0.1.0-3 - update * Tue Aug 18 2026 Jonathan Wright - 0.1.0-2 - update * Mon Jul 27 2026 AlmaLinux Certification SIG - 0.1.0-1 - Initial package: rewritten Python suite replacing the Ansible prototype