#!/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"