From e7c0566efebaee331f4e6d22f1afea5aa4c8baaa Mon Sep 17 00:00:00 2001 From: rookiestar28 Date: Wed, 8 Apr 2026 02:34:34 +0800 Subject: [PATCH] fix(ci): restore defusedxml dependency parity --- .github/workflows/ci.yml | 2 +- requirements.txt | 1 + scripts/pre_push_checks.sh | 5 ++ scripts/preflight_check.py | 3 +- scripts/run_full_tests_linux.sh | 5 ++ scripts/run_full_tests_windows.ps1 | 11 ++++ tests/test_r120_dependency_parity_contract.py | 52 +++++++++++++++++++ 7 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 tests/test_r120_dependency_parity_contract.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e26d20a..7903301 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: - name: Install preflight deps run: | python -m pip install --upgrade pip - python -m pip install cryptography + python -m pip install -r requirements.txt - name: R120 preflight run: | python scripts/preflight_check.py --strict diff --git a/requirements.txt b/requirements.txt index dae35ee..0a93621 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ # Keep this file aligned with `pyproject.toml` project.dependencies. # cryptography>=41.0 +defusedxml>=0.7.1 diff --git a/scripts/pre_push_checks.sh b/scripts/pre_push_checks.sh index 0259218..31907d1 100644 --- a/scripts/pre_push_checks.sh +++ b/scripts/pre_push_checks.sh @@ -213,6 +213,11 @@ if ! "$VENV_PY" -c "import cryptography" >/dev/null 2>&1; then echo "[pre-push] INFO: installing cryptography into project venv ($VENV_DIR) ..." >&2 pip_install_or_fail "required for S57 secrets-at-rest encryption paths/tests" cryptography fi +if ! "$VENV_PY" -c "import defusedxml" >/dev/null 2>&1; then + # IMPORTANT: keep local/CI parity with requirements.txt and WeChat ingress tests. + echo "[pre-push] INFO: installing defusedxml into project venv ($VENV_DIR) ..." >&2 + pip_install_or_fail "required for S85 fail-closed XML parsing paths/tests" defusedxml +fi require_cmd npm diff --git a/scripts/preflight_check.py b/scripts/preflight_check.py index cf3e217..5e7f358 100644 --- a/scripts/preflight_check.py +++ b/scripts/preflight_check.py @@ -6,7 +6,7 @@ Validates the build environment before deployment or test execution. Checks: 1. Python version (>=3.10) 2. Node.js version (>=18.0.0, per package.json + TEST_SOP) -3. Essential Python dependencies (cryptography) +3. Essential Python dependencies (cryptography, defusedxml) Usage: python scripts/preflight_check.py [--strict] @@ -23,6 +23,7 @@ MIN_NODE_VERSION = (18, 0, 0) REQUIRED_PYTHON_PACKAGES = [ ("cryptography", "41.0"), + ("defusedxml", "0.7.1"), ] # Colors for output diff --git a/scripts/run_full_tests_linux.sh b/scripts/run_full_tests_linux.sh index f8f0235..3531881 100644 --- a/scripts/run_full_tests_linux.sh +++ b/scripts/run_full_tests_linux.sh @@ -123,6 +123,11 @@ if ! "$VENV_PY" -c "import cryptography" >/dev/null 2>&1; then echo "[tests] Installing cryptography into project venv ($VENV_DIR) ..." pip_install_or_fail "required for S57 secrets-at-rest encryption tests" cryptography fi +if ! "$VENV_PY" -c "import defusedxml" >/dev/null 2>&1; then + # IMPORTANT: keep local full-test bootstrap aligned with requirements.txt. + echo "[tests] Installing defusedxml into project venv ($VENV_DIR) ..." + pip_install_or_fail "required for S85 fail-closed XML parsing tests" defusedxml +fi NODE_MAJOR="$(node -p "process.versions.node.split('.')[0]")" if [ "$NODE_MAJOR" -lt 18 ]; then diff --git a/scripts/run_full_tests_windows.ps1 b/scripts/run_full_tests_windows.ps1 index d1157a7..1cdfa6d 100644 --- a/scripts/run_full_tests_windows.ps1 +++ b/scripts/run_full_tests_windows.ps1 @@ -169,6 +169,17 @@ if (-not $hasCryptography) { Invoke-Checked "pip install cryptography" { & $venvPython -m pip install cryptography } } +$hasDefusedXml = $true +& $venvPython -c "import defusedxml" | Out-Null +if ($LASTEXITCODE -ne 0) { + $hasDefusedXml = $false +} +if (-not $hasDefusedXml) { + # IMPORTANT: keep Windows full-test bootstrap aligned with requirements.txt. + Write-Host "[tests] Installing defusedxml into project venv (S85 fail-closed XML parsing) ..." + Invoke-Checked "pip install defusedxml" { & $venvPython -m pip install defusedxml } +} + # Ensure Node >= 18 $nodeMajor = [int]((& node -p "process.versions.node.split('.')[0]").Trim()) if ($nodeMajor -lt 18) { diff --git a/tests/test_r120_dependency_parity_contract.py b/tests/test_r120_dependency_parity_contract.py new file mode 100644 index 0000000..37172f4 --- /dev/null +++ b/tests/test_r120_dependency_parity_contract.py @@ -0,0 +1,52 @@ +import ast +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +REQUIREMENTS = ROOT / "requirements.txt" +PRE_PUSH = ROOT / "scripts" / "pre_push_checks.sh" +FULL_TESTS_LINUX = ROOT / "scripts" / "run_full_tests_linux.sh" +FULL_TESTS_WINDOWS = ROOT / "scripts" / "run_full_tests_windows.ps1" +PREFLIGHT = ROOT / "scripts" / "preflight_check.py" +PYPROJECT = ROOT / "pyproject.toml" +CI_WORKFLOW = ROOT / ".github" / "workflows" / "ci.yml" + + +def _parse_required_python_packages(): + module = ast.parse(PREFLIGHT.read_text(encoding="utf-8")) + for node in module.body: + if not isinstance(node, ast.Assign): + continue + for target in node.targets: + if isinstance(target, ast.Name) and target.id == "REQUIRED_PYTHON_PACKAGES": + return ast.literal_eval(node.value) + raise AssertionError("REQUIRED_PYTHON_PACKAGES missing from scripts/preflight_check.py") + + +class DependencyParityContractTests(unittest.TestCase): + def test_requirements_stays_aligned_with_declared_runtime_dependencies(self): + requirements = REQUIREMENTS.read_text(encoding="utf-8") + pyproject = PYPROJECT.read_text(encoding="utf-8") + + self.assertIn("cryptography>=41.0", requirements) + self.assertIn("defusedxml>=0.7.1", requirements) + self.assertIn('dependencies = ["cryptography>=41.0", "defusedxml>=0.7.1"]', pyproject) + + def test_preflight_declares_all_essential_runtime_packages(self): + required = _parse_required_python_packages() + self.assertIn(("cryptography", "41.0"), required) + self.assertIn(("defusedxml", "0.7.1"), required) + + def test_local_acceptance_bootstraps_install_defusedxml(self): + self.assertIn("import defusedxml", PRE_PUSH.read_text(encoding="utf-8")) + self.assertIn("pip install defusedxml", FULL_TESTS_WINDOWS.read_text(encoding="utf-8")) + self.assertIn("import defusedxml", FULL_TESTS_LINUX.read_text(encoding="utf-8")) + + def test_frontend_e2e_preflight_uses_requirements_contract(self): + workflow = CI_WORKFLOW.read_text(encoding="utf-8") + self.assertIn("python -m pip install -r requirements.txt", workflow) + + +if __name__ == "__main__": + unittest.main()