mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-02 18:17:11 +09:00
Seven distro images were green throughout issue #99 because every one of them runs the unit suites against STUBS. That is the right trade for logic, but it left the one thing that talks to real package repos completely untested — so a new Debian release dropping the Python we ask for could only ever be found by a user. scripts/test/test_provision_python.sh runs identify_system, choose_pkg_manager and provision_python for real, then proves the result is usable: in range, can create a virtualenv, has working pip, and imports ssl/sqlite3/ctypes. It needs no systemd, nginx or ports, so it fits the plain containers the matrix already pulls. Reaching the source build counts as a FAILURE, not a pass: a distro that gets there is one we have not really added support for. Wired up as a new `python-provisioning` job over the same images, and debian:13 added to both matrices. Adding trixie to the existing matrix alone would have been decorative — those legs would have gone green on a distro that cannot install. Proven in both directions before being relied on: against the pre-fix tree it reproduced #99 exactly on debian:13 (the reporter's apt errors, the fall through to a source build, and the abort message), while debian:12 passed 7/7. Also fixed the suite pinning the bug. T2 asserted `ver_in_range 3.13` must be FALSE and T1's stub used 3.13 as its "too new" case, so the gate could not be widened without the tests failing. Both moved to 3.14, plus a direct guard on PYTHON_MAX — otherwise a regression to 3.12 could be "fixed" by editing the assertion to match. 122 passed, 0 failed. full-stack-test.ps1 documents why debian13 is NOT in the VM harness: no trixie box publishes a Hyper-V provider (generic/debian13 does not exist, debian/trixie64 is libvirt-only, bento/debian-13 has no hyperv) and Multipass is Ubuntu-only. Named there with what remains uncovered on trixie — the full systemd + nginx + API end-to-end — rather than left as a silent gap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
157 lines
6.5 KiB
Bash
157 lines
6.5 KiB
Bash
#!/usr/bin/env bash
|
||
#
|
||
# Does install.sh actually obtain a working Python ON THIS DISTRO?
|
||
#
|
||
# Every other suite here runs against stubs, which is the right trade for logic
|
||
# but means the one thing that talks to the distro's real package repos was
|
||
# never exercised. Issue #99: Debian 13 ships only python3.13, the version gate
|
||
# accepted 3.11-3.12, so both `apt-get install python3.11` and `python3.12`
|
||
# failed and the installer fell through to a source build that cannot work
|
||
# (nothing in install.sh ever installs a compiler). Seven distro images in CI
|
||
# and none of them caught it, because none of them ran this code for real.
|
||
#
|
||
# So: run identify_system + choose_pkg_manager + provision_python against the
|
||
# real repos, then prove the result is usable. No systemd, no nginx, no network
|
||
# services — this fits in the plain distro containers the matrix already pulls.
|
||
#
|
||
# Destructive: installs packages. Intended for a container or a throwaway VM.
|
||
# Refuses to run on a box that looks like somebody's machine unless forced.
|
||
#
|
||
# Run: bash scripts/test/test_provision_python.sh
|
||
# SERVERKIT_ALLOW_DESTRUCTIVE=1 bash scripts/test/test_provision_python.sh
|
||
set -uo pipefail
|
||
|
||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
REPO_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||
INSTALL_SH="$REPO_DIR/install.sh"
|
||
|
||
PASS=0
|
||
FAIL=0
|
||
SKIP=0
|
||
ok() { PASS=$((PASS + 1)); printf ' \033[32m✔\033[0m %s\n' "$1"; }
|
||
bad() { FAIL=$((FAIL + 1)); printf ' \033[31m✘\033[0m %s\n' "$1"; }
|
||
skip() { SKIP=$((SKIP + 1)); printf ' \033[33m∼\033[0m %s (skipped)\n' "$1"; }
|
||
|
||
# --------------------------------------------------------------------------
|
||
# Guard rails. This one really does apt/dnf install.
|
||
# --------------------------------------------------------------------------
|
||
if [ "${SERVERKIT_ALLOW_DESTRUCTIVE:-0}" != "1" ] \
|
||
&& [ ! -f /.dockerenv ] && [ -z "${CI:-}" ]; then
|
||
printf 'Refusing to run: this installs packages system-wide.\n' >&2
|
||
printf 'Run it in a container, or set SERVERKIT_ALLOW_DESTRUCTIVE=1.\n' >&2
|
||
exit 2
|
||
fi
|
||
if [ "$(id -u)" != "0" ]; then
|
||
printf 'Refusing to run: package installation needs root.\n' >&2
|
||
exit 2
|
||
fi
|
||
|
||
printf '\nprovision_python against real package repos\n'
|
||
|
||
# shellcheck source=/dev/null
|
||
source "$INSTALL_SH" >/dev/null 2>&1
|
||
# install.sh runs `set -euo pipefail` at the top, and sourcing applies that to
|
||
# THIS shell. Under -e the first probe that returns non-zero (which is the
|
||
# normal way half of these functions report "not found") would kill the run
|
||
# before printing a single assertion. Put it back the way a test harness needs.
|
||
set +e
|
||
|
||
# The installer's own detection, not a guess — provision_python branches on
|
||
# OS_FAMILY and ID, and choose_pkg_manager is what makes pkg_add do anything.
|
||
identify_system >/dev/null 2>&1 || true
|
||
choose_pkg_manager >/dev/null 2>&1 || true
|
||
|
||
printf ' distro: %s (ID=%s, family=%s, pkg=%s)\n' \
|
||
"${PRETTY_NAME:-unknown}" "${ID:-?}" "${OS_FAMILY:-?}" "${PKG_MGR:-?}"
|
||
printf ' gate: Python %s-%s\n' "$PYTHON_MIN" "$PYTHON_MAX"
|
||
|
||
if [ "${PKG_MGR:-}" = "" ]; then
|
||
skip "no package manager detected — nothing to provision with"
|
||
printf '\n%d passed, %d failed, %d skipped\n\n' "$PASS" "$FAIL" "$SKIP"
|
||
[ "$FAIL" -eq 0 ]
|
||
exit $?
|
||
fi
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 1. A supported interpreter must be obtainable from the distro's own repos.
|
||
#
|
||
# The source-build fallback is deliberately NOT accepted as a pass here. It
|
||
# takes 20+ minutes with --enable-optimizations, needs a toolchain the
|
||
# installer never installs, and swallows its own errors through `| tail -1`.
|
||
# A distro that reaches it is a distro we have not really added support for.
|
||
# --------------------------------------------------------------------------
|
||
before_build_from_source=0
|
||
build_python_from_source() {
|
||
before_build_from_source=1
|
||
return 1
|
||
}
|
||
|
||
# provision_python's last line is `... || halt "Could not install a supported
|
||
# Python"`, and halt exits. Unstubbed it would take this harness down with it
|
||
# before a single assertion printed, turning a real failure into silence.
|
||
# Returning 1 instead lets provision_python report failure normally.
|
||
halted=""
|
||
halt() {
|
||
halted="$*"
|
||
return 1
|
||
}
|
||
|
||
if provision_python >/tmp/pp.log 2>&1; then
|
||
prov_rc=0
|
||
else
|
||
prov_rc=$?
|
||
fi
|
||
sed 's/^/ | /' /tmp/pp.log | tail -25
|
||
|
||
if [ "$before_build_from_source" = "1" ]; then
|
||
bad "no packaged Python in $PYTHON_MIN-$PYTHON_MAX on ${ID:-this distro} — the installer resorts to compiling one"
|
||
[ -n "$halted" ] && printf ' installer would abort with: %s\n' "$halted"
|
||
elif [ "$prov_rc" != "0" ]; then
|
||
bad "provision_python failed (exit $prov_rc)${halted:+ — $halted}"
|
||
else
|
||
ok "provision_python obtained a supported Python from the distro's packages"
|
||
fi
|
||
|
||
# --------------------------------------------------------------------------
|
||
# 2. Whatever it picked has to actually be usable.
|
||
# A python that cannot build a venv or run pip fails the install later, at a
|
||
# much more confusing moment.
|
||
# --------------------------------------------------------------------------
|
||
if [ -z "${PYTHON_BIN:-}" ] || ! command -v "$PYTHON_BIN" >/dev/null 2>&1; then
|
||
bad "PYTHON_BIN is unset or not on PATH after provision_python"
|
||
else
|
||
pyver="$("$PYTHON_BIN" -c 'import sys;print(".".join(map(str,sys.version_info[:2])))' 2>/dev/null || echo '?')"
|
||
if ver_in_range "$pyver"; then
|
||
ok "$PYTHON_BIN reports $pyver, inside the supported range"
|
||
else
|
||
bad "$PYTHON_BIN reports $pyver, outside $PYTHON_MIN-$PYTHON_MAX"
|
||
fi
|
||
|
||
if "$PYTHON_BIN" -m venv /tmp/pp-venv >/dev/null 2>&1 \
|
||
&& [ -x /tmp/pp-venv/bin/python ]; then
|
||
ok "it can create a virtualenv"
|
||
if /tmp/pp-venv/bin/python -m pip --version >/dev/null 2>&1; then
|
||
ok "the virtualenv has a working pip"
|
||
else
|
||
bad "the virtualenv has no working pip (missing ensurepip?)"
|
||
fi
|
||
# ssl and sqlite3 are the two stdlib modules a hand-built or
|
||
# stripped-down Python most often lacks, and both fail far away from
|
||
# here: no ssl means every HTTPS fetch dies, no sqlite3 means the
|
||
# database never opens.
|
||
for mod in ssl sqlite3 ctypes; do
|
||
if /tmp/pp-venv/bin/python -c "import $mod" >/dev/null 2>&1; then
|
||
ok "stdlib $mod is present"
|
||
else
|
||
bad "stdlib $mod is MISSING — the panel will fail later, not here"
|
||
fi
|
||
done
|
||
else
|
||
bad "it cannot create a virtualenv"
|
||
fi
|
||
rm -rf /tmp/pp-venv
|
||
fi
|
||
|
||
printf '\n%d passed, %d failed, %d skipped\n\n' "$PASS" "$FAIL" "$SKIP"
|
||
[ "$FAIL" -eq 0 ]
|