python/required-symbols/check.sh

92 lines
3.3 KiB
Bash
Executable file

#!/bin/bash
# Compare required external symbols of compiled Python extension modules between
# the PR build (pre-installed by Testing Farm) and the latest stable version
# available in the distribution repositories.
#
# The PR build is already installed before this test runs (via the Testing Farm
# artifact mechanism). We save its .so files, downgrade to the stable repo version,
# collect symbols from both, and compare.
#
# Output:
# + [module] symbol — PR build adds a new required symbol (potential issue possibly requiring update of Requires)
# - [module] symbol — PR build drops a previously required symbol (generally OK)
#
# Exit codes:
# 0 — no changes, or no stable version available to compare against (skipped)
# 1 — symbol differences detected between PR build and stable release
set -eo pipefail
PYVER=${VERSION}
SCRIPT_DIR="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
WORK_DIR=""
PKGS_MODIFIED=false
cleanup() {
if [[ "$PKGS_MODIFIED" == true ]]; then
echo ""
echo "=== Reverting package changes ==="
dnf history undo last -y 2>&1 || echo "WARNING: dnf history undo failed" >&2
fi
[[ -n "$WORK_DIR" ]] && rm -rf "$WORK_DIR"
}
trap cleanup EXIT
# Directories containing compiled extension modules
SCAN_DIRS=()
for d in "/usr/lib64/python${PYVER}" "/usr/lib64/python${PYVER}t"; do
[ -d "$d" ] && SCAN_DIRS+=("$d")
done
if [ ${#SCAN_DIRS[@]} -eq 0 ]; then
echo "ERROR: No Python extension directories found for python${PYVER}" >&2
exit 1
fi
SCAN_ARGS=("${SCAN_DIRS[@]}")
for f in /usr/lib64/libpython${PYVER}*.so.*; do
[[ -f "$f" && ! -L "$f" ]] && SCAN_ARGS+=("$f")
done
WORK_DIR=$(mktemp -d)
echo "=== Collecting PR build symbols ==="
python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_ARGS[@]}" > "$WORK_DIR/new.json"
echo ""
echo "=== Available repos ==="
dnf repolist --all
# Discover packages dynamically from the installed source RPM so we don't need
# to hardcode subpackage names — works regardless of which subpackages were built.
readarray -t PKGS < <(
rpm -qa --qf '%{SOURCERPM} %{NAME}\n' |
awk -v src="python${PYVER}" '$1 ~ "^" src "-[0-9]" { print $2 }' |
sort -u
)
echo "Packages to install from stable repos: ${PKGS[*]}"
# Install the stable version from distribution repos, excluding the artifact repo
# that carries the PR build, so we always compare against what is publicly available.
#
# Strategy:
# 1. dnf reinstall — works when the PR did not bump NVR (same version in stable repos)
# 2. dnf distro-sync — fallback when the PR bumped NVR; installs the latest stable NVR
echo ""
echo "=== Installing stable version from repos ==="
if ! dnf reinstall -y --disablerepo=test-artifacts "${PKGS[@]}" 2>&1; then
echo "INFO: reinstall failed (NVR not in stable repos), falling back to distro-sync" >&2
if ! dnf distro-sync -y --disablerepo=test-artifacts "${PKGS[@]}" 2>&1; then
echo "INFO: distro-sync failed; no stable version available, skipping comparison." >&2
exit 0
fi
fi
PKGS_MODIFIED=true
echo ""
echo "=== Collecting stable version symbols ==="
python${PYVER} "$SCRIPT_DIR/collect_symbols.py" "${SCAN_ARGS[@]}" > "$WORK_DIR/old.json"
echo ""
echo "=== Comparing PR build against stable ==="
python${PYVER} "$SCRIPT_DIR/compare_symbols.py" "$WORK_DIR/old.json" "$WORK_DIR/new.json"