159 lines
6.4 KiB
Bash
Executable File
159 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
|
# Copyright (C) 2026 Bryan Joshua Pedini
|
|
|
|
# fixture.sh - deterministic E2E fixture for RPM distros (dnf: Alma/Rocky/..., zypper: openSUSE)
|
|
#
|
|
# Builds three dummy packages with rpmbuild, installs version 1.0 of each, publishes
|
|
# version 2.0 in a local file:// repo and registers that repo, so that the package
|
|
# manager reports exactly these upgrades:
|
|
#
|
|
# su-dummy-a 1.0-1 -> 2.0-1 (noarch, Requires: su-dummy-b)
|
|
# su-dummy-b 1.0-1 -> 2.0-1 (noarch)
|
|
# su-dummy-c 1.0-1 -> 2.0-1 (ARCH-SPECIFIC, so the ".x86_64" suffix path is exercised)
|
|
#
|
|
# Optional (SU_FIXTURE_EXTRAS=1):
|
|
# - installs su-dummy-d 1.0 (NOT upgradable): Requires su-dummy-a, /bin/sh (file dep),
|
|
# "su-dummy-b >= 1.0" (versioned), "(su-dummy-c if su-dummy-b)" (rich dep) and rpmlib(...)
|
|
# -> for reverse-dep / dependency-closure tests;
|
|
# - publishes su-dummy-r 1.0 (Requires su-dummy-s) and su-dummy-s 1.0 in the repo, NOT installed
|
|
# -> for install/remove/--clean-deps tests.
|
|
#
|
|
# Re-runnable: re-running resets everything to the "1.0 installed, 2.0 available" state.
|
|
#
|
|
# Usage (inside the container, as root): bash fixture.sh
|
|
# docker run --rm -i -v <project>/discovery/rpm:/d:ro <image> bash /d/fixture.sh
|
|
set -euo pipefail
|
|
export LC_ALL=C
|
|
|
|
T0=$SECONDS
|
|
log() { printf '[fixture] %s\n' "$*" >&2; }
|
|
die() { printf '[fixture] ERROR: %s\n' "$*" >&2; exit 1; }
|
|
[ "$(id -u)" -eq 0 ] || die "must run as root"
|
|
|
|
# ---- 1. backend detection ---------------------------------------------------
|
|
BACKEND=
|
|
if command -v zypper >/dev/null 2>&1; then BACKEND=zypper
|
|
elif command -v dnf >/dev/null 2>&1; then BACKEND=dnf
|
|
elif command -v yum >/dev/null 2>&1; then BACKEND=yum
|
|
else die "neither zypper nor dnf/yum found"; fi
|
|
log "backend=$BACKEND"
|
|
|
|
TOP=/var/tmp/su-fixture # rpmbuild topdir
|
|
REPO=/var/lib/su-dummy-repo # file:// repo (only 2.0 packages)
|
|
SPEC_DIR=$TOP/SPECS
|
|
|
|
# ---- 2. build tools ---------------------------------------------------------
|
|
t=$SECONDS
|
|
case $BACKEND in
|
|
zypper) zypper --non-interactive --quiet install --no-recommends rpm-build createrepo_c >/dev/null ;;
|
|
dnf|yum) "$BACKEND" -y -q install --setopt=install_weak_deps=False rpm-build createrepo_c >/dev/null ;;
|
|
esac
|
|
command -v rpmbuild >/dev/null || die "rpmbuild missing after install"
|
|
command -v createrepo_c >/dev/null || die "createrepo_c missing after install"
|
|
log "build tools installed in $((SECONDS - t))s"
|
|
|
|
# ---- 3. build RPMs ----------------------------------------------------------
|
|
rm -rf "$TOP" "$REPO"
|
|
mkdir -p "$TOP"/{BUILD,BUILDROOT,RPMS,SRPMS,SOURCES,SPECS} "$REPO"
|
|
|
|
# mkspec <name> <version> <arch|noarch> <extra spec header lines...>
|
|
mkspec() {
|
|
local name=$1 ver=$2 arch=$3; shift 3
|
|
local f=$SPEC_DIR/$name-$ver.spec
|
|
{
|
|
echo "Name: $name"
|
|
echo "Version: $ver"
|
|
echo "Release: 1"
|
|
echo "Summary: skeptical-updater dummy $name (v$ver)"
|
|
echo "License: MIT"
|
|
[ "$arch" = noarch ] && echo "BuildArch: noarch"
|
|
local l; for l in "$@"; do echo "$l"; done
|
|
echo "%global debug_package %{nil}"
|
|
echo "%description"
|
|
echo "Dummy package $name version $ver for skeptical-updater tests."
|
|
echo ""
|
|
echo "Second paragraph of $name $ver (blank line above on purpose)."
|
|
echo "%install"
|
|
echo "mkdir -p %{buildroot}/usr/share/su-dummy"
|
|
echo "echo '$name $ver' > %{buildroot}/usr/share/su-dummy/$name"
|
|
echo "%files"
|
|
echo "/usr/share/su-dummy/$name"
|
|
} > "$f"
|
|
printf '%s\n' "$f"
|
|
}
|
|
|
|
build() { rpmbuild -bb --quiet --define "_topdir $TOP" "$1" >/dev/null 2>"$TOP/rpmbuild.err" \
|
|
|| { cat "$TOP/rpmbuild.err" >&2; die "rpmbuild failed for $1"; }; }
|
|
|
|
t=$SECONDS
|
|
for v in 1.0 2.0; do
|
|
build "$(mkspec su-dummy-a $v noarch 'Requires: su-dummy-b')"
|
|
build "$(mkspec su-dummy-b $v noarch)"
|
|
build "$(mkspec su-dummy-c $v arch)"
|
|
done
|
|
if [ "${SU_FIXTURE_EXTRAS:-0}" = 1 ]; then
|
|
build "$(mkspec su-dummy-s 1.0 noarch)" # published in the repo, NOT installed
|
|
build "$(mkspec su-dummy-r 1.0 noarch 'Requires: su-dummy-s')" # (used to test remove/--clean-deps)
|
|
build "$(mkspec su-dummy-d 1.0 noarch \
|
|
'Requires: su-dummy-a' \
|
|
'Requires: /bin/sh' \
|
|
'Requires: su-dummy-b >= 1.0' \
|
|
'Requires: (su-dummy-c if su-dummy-b)' \
|
|
'Requires: rpmlib(CompressedFileNames)')"
|
|
fi
|
|
log "rpmbuild done in $((SECONDS - t))s"
|
|
|
|
# ---- 4. install 1.0 (rpm directly: distro-agnostic, resets to 1.0 on re-run) ----
|
|
shopt -s nullglob
|
|
v1=( "$TOP"/RPMS/*/su-dummy-[abc]-1.0-1.*.rpm )
|
|
[ "${#v1[@]}" -eq 3 ] || die "expected 3 built 1.0 rpms, got ${#v1[@]}"
|
|
rpm -Uvh --oldpackage --replacepkgs "${v1[@]}" >/dev/null
|
|
if [ "${SU_FIXTURE_EXTRAS:-0}" = 1 ]; then
|
|
rpm -Uvh --oldpackage --replacepkgs "$TOP"/RPMS/noarch/su-dummy-d-1.0-1.*.rpm >/dev/null
|
|
fi
|
|
|
|
# ---- 5. publish 2.0 as a local repo -------------------------------------------
|
|
v2=( "$TOP"/RPMS/*/su-dummy-[abc]-2.0-1.*.rpm )
|
|
[ "${#v2[@]}" -eq 3 ] || die "expected 3 built 2.0 rpms, got ${#v2[@]}"
|
|
cp "${v2[@]}" "$REPO"/
|
|
if [ "${SU_FIXTURE_EXTRAS:-0}" = 1 ]; then cp "$TOP"/RPMS/noarch/su-dummy-[rs]-1.0-1.*.rpm "$REPO"/; fi
|
|
createrepo_c --quiet "$REPO" >/dev/null
|
|
|
|
# ---- 6. register repo ---------------------------------------------------------
|
|
case $BACKEND in
|
|
zypper)
|
|
zypper --non-interactive --quiet removerepo su-dummy >/dev/null 2>&1 || true
|
|
zypper --non-interactive --quiet addrepo --no-gpgcheck --no-check --refresh "file://$REPO" su-dummy >/dev/null
|
|
t=$SECONDS
|
|
zypper --non-interactive --quiet refresh su-dummy >/dev/null
|
|
;;
|
|
dnf|yum)
|
|
cat > /etc/yum.repos.d/su-dummy.repo <<REPOEOF
|
|
[su-dummy]
|
|
name=skeptical-updater dummy packages
|
|
baseurl=file://$REPO
|
|
enabled=1
|
|
gpgcheck=0
|
|
repo_gpgcheck=0
|
|
metadata_expire=0
|
|
REPOEOF
|
|
t=$SECONDS
|
|
"$BACKEND" -q makecache --repo su-dummy >/dev/null
|
|
;;
|
|
esac
|
|
log "repo refresh in $((SECONDS - t))s"
|
|
|
|
# ---- 7. verify: the three must be listed as upgradable --------------------------
|
|
want="su-dummy-a su-dummy-b su-dummy-c"
|
|
case $BACKEND in
|
|
zypper) listing=$(zypper --non-interactive --quiet list-updates 2>&1 || true) ;;
|
|
dnf|yum) listing=$("$BACKEND" -q check-update 2>&1 || true) ;;
|
|
esac
|
|
bad=0
|
|
for p in $want; do
|
|
printf '%s\n' "$listing" | grep -Eq "(^|[| ])$p([. ]|$)" || { log "NOT listed as upgradable: $p"; bad=1; }
|
|
done
|
|
if [ "$bad" -ne 0 ]; then printf '%s\n' "$listing" >&2; die "fixture verification failed"; fi
|
|
log "OK: su-dummy-{a,b,c} 1.0 installed and 2.0 offered by repo 'su-dummy' (total ${SECONDS}s, since start $((SECONDS - T0))s)"
|