Check the installed packages against the lockfiles
A clean lockfile does not mean a clean workstation. pip-audit reported vulnerabilities here while requirements.txt was already pinned to fixed versions - the installed environment had drifted from the pins, and nothing compared the two. The same drift had left requirements-dev.txt uninstalled, so pytest.ini's -n auto failed for want of pytest-xdist. Add scripts/check-dependency-drift.py, comparing installed distributions against both lockfiles. MISSING is reported separately from MISMATCHED: a package that was never installed and a package at the wrong version need different fixes, and folding them together hid the missing dev requirements. Wire it into the style gate advisorially, with DEPENDENCY_DRIFT_ENFORCE=1 for CI. A developer mid-upgrade should not be blocked by a gate that is telling them something they already know. The gate now probes python, python3 and py by executing them rather than by looking them up on PATH. On Windows, python3 resolves to the Microsoft Store alias stub, which is present on PATH and does nothing when run. CONTRIBUTING.md gains the Windows-specific traps this exposed: npm.cmd rather than npm under a Restricted execution policy, the explicit Git Bash path because bare bash finds the WSL shim, and npm test rather than npx vitest run because only the pretest hook stages plugin frontends.
This commit is contained in:
@@ -608,6 +608,67 @@ if [ -n "$SITE_HITS" ]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Dependency drift -------------------------------------------------------
|
||||
#
|
||||
# Not a naming rule, but it belongs to the same question: can this run be
|
||||
# trusted. A stale environment makes every OTHER check here a statement about
|
||||
# libraries that are not the ones the lockfile specifies.
|
||||
#
|
||||
# ADVISORY BY DEFAULT, and deliberately so. Set DEPENDENCY_DRIFT_ENFORCE=1 to
|
||||
# fail the gate (CI should). Two considerations pull against each other here:
|
||||
# a warning nobody reads is worthless, but a blocking check wedges anyone who
|
||||
# is mid-upgrade and legitimately holds an unpinned version for an afternoon.
|
||||
# The tie-breaker is the comment above the site-literal scan: this file has
|
||||
# twice acquired a reasonable-looking flag that silently disabled a check, once
|
||||
# for months with the gate green. A check that ANNOUNCES it is advisory cannot
|
||||
# become that, because its output changes the moment drift appears.
|
||||
#
|
||||
# Skipped when python is absent rather than failing: this script is also run on
|
||||
# machines that only build the frontend.
|
||||
#
|
||||
# Each candidate is TEST-EXECUTED, not merely located with command -v. On
|
||||
# Windows, "python3" resolves to the Microsoft Store app-execution alias - a
|
||||
# zero-byte stub that exists on PATH, satisfies command -v, and then prints
|
||||
# "Python was not found; run without arguments to install from the Microsoft
|
||||
# Store" to stderr and exits non-zero. Locating it and trusting it reported the
|
||||
# drift check as failing on a machine whose environment was in fact clean.
|
||||
DEPENDENCY_DRIFT_ENFORCE=${DEPENDENCY_DRIFT_ENFORCE:-0}
|
||||
echo "==> Checking installed packages against the lockfiles..."
|
||||
DRIFT_PYTHON=""
|
||||
for CANDIDATE in python python3 py; do
|
||||
if command -v "$CANDIDATE" >/dev/null 2>&1 \
|
||||
&& "$CANDIDATE" -c 'import sys' >/dev/null 2>&1; then
|
||||
DRIFT_PYTHON="$CANDIDATE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# The checker and the lockfiles must both be present. They are not when the gate
|
||||
# is copied alone into a throwaway tree, which is exactly what
|
||||
# tests/test_naming_gate_migrations.py does to exercise the non-git fallback
|
||||
# branch. Absent inputs mean "nothing to compare", not "drift".
|
||||
if [ ! -f scripts/check-dependency-drift.py ] || [ ! -f requirements.txt ]; then
|
||||
DRIFT_PYTHON=""
|
||||
fi
|
||||
if [ -z "$DRIFT_PYTHON" ]; then
|
||||
echo " python not found, skipping dependency drift check"
|
||||
echo
|
||||
else
|
||||
DRIFT_OUTPUT=$("$DRIFT_PYTHON" scripts/check-dependency-drift.py --quiet 2>&1) || DRIFT_STATUS=$?
|
||||
DRIFT_STATUS=${DRIFT_STATUS:-0}
|
||||
if [ "$DRIFT_STATUS" -ne 0 ]; then
|
||||
if [ "$DEPENDENCY_DRIFT_ENFORCE" = "1" ]; then
|
||||
echo "$DRIFT_OUTPUT"
|
||||
echo
|
||||
VIOLATIONS=$((VIOLATIONS + 1))
|
||||
else
|
||||
echo " dependency drift found (report only, not failing):"
|
||||
echo "$DRIFT_OUTPUT" | sed 's/^/ /'
|
||||
echo " Set DEPENDENCY_DRIFT_ENFORCE=1 to make this fail the gate."
|
||||
echo
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$VIOLATIONS" -gt 0 ]; then
|
||||
echo "=================================================="
|
||||
echo "$VIOLATIONS naming/style violation(s) found."
|
||||
|
||||
Reference in New Issue
Block a user