#!/usr/bin/env bash # ===================================================================== # pbl_evidence 测试环境引导(发现项 DISC.2) # # 背景:测试机宿主 python3 **没有** pytest,`python3 -m pytest` 直接 # `No module named pytest`。此前靠临时注入 # PYTHONPATH=/.pylibs(pytest 9.1.1)跑通,属一次性手段, # 不可复现、且会让 test 角色误判为「用例失败/环境不可用」。 # # 本脚本把「测试前置依赖」变成显式、可重放的步骤: # 0) 仓库卫生门禁:确认 venv 落点已被 .gitignore 真正忽略 # (`git check-ignore -q`,不靠注释自称)——否则本脚本会往版本库里 # 灌 1171 个 site-packages 文件(QC #1 实测事故,见下「历史」) # 1) 建独立测试 venv(默认 .venv-test,可用 PBL_EVIDENCE_TEST_VENV 改位置) # 2) 安装 requirements-dev.txt(== pyproject [project.optional-dependencies].test) # 3) 打印 pytest 版本自证可用 # # 历史(QC #1 退回,已修正):`.venv-test/` 曾被 commit 0a87e13 误入库 # (`git ls-files | grep -c venv-test` = 1171),而当时 .gitignore 只有 # `.venv/`、`venv/` 两条,本脚本注释却声称「已在 .gitignore 排除」——注释与 # 实测矛盾。现已 ① .gitignore 补 `.venv-test/`;② `git rm -r --cached .venv-test` # 清出版本跟踪(本地文件保留);③ 本脚本第 0 步把「被忽略」变成**可执行断言**, # 不再靠注释。自证: # git check-ignore -v .venv-test # 命中 .gitignore 规则 # git ls-files | grep -c 'venv-test' # 必须为 0 # # 用法: # bash scripts/setup_test_env.sh # PBL_EVIDENCE_TEST_VENV=/tmp/ev-venv bash scripts/setup_test_env.sh # 换 venv 位置 # PBL_EVIDENCE_INSTALL_PKG=1 bash scripts/setup_test_env.sh # 顺带 pip install -e . # PBL_EVIDENCE_SKIP_GITIGNORE_CHECK=1 bash scripts/setup_test_env.sh # 非 git 环境(如 CI 解包目录) # # 幂等:venv 已存在则复用,pip 已满足则秒退;可被 build.sh / CI 反复调用。 # ===================================================================== set -euo pipefail MODULE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" VENV="${PBL_EVIDENCE_TEST_VENV:-${MODULE_ROOT}/.venv-test}" REQ_DEV="${MODULE_ROOT}/requirements-dev.txt" HOST_PY="${PBL_EVIDENCE_HOST_PYTHON:-$(command -v python3)}" log() { echo "[setup_test_env] $*"; } die() { echo "[setup_test_env][FATAL] $*" >&2; exit 1; } [ -f "${REQ_DEV}" ] || die "缺少测试依赖清单 ${REQ_DEV}" [ -x "${HOST_PY}" ] || die "找不到宿主 python3(可用 PBL_EVIDENCE_HOST_PYTHON 指定)" # ---- 0. 仓库卫生门禁:venv 落点必须真的被 .gitignore 忽略 ---- # 只在 venv 位于模块仓库内时检查(放到仓库外如 /tmp 天然不会污染版本库)。 if [ "${PBL_EVIDENCE_SKIP_GITIGNORE_CHECK:-0}" != "1" ] \ && [ "${VENV#${MODULE_ROOT}/}" != "${VENV}" ] \ && git -C "${MODULE_ROOT}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then REL="${VENV#${MODULE_ROOT}/}" if ! git -C "${MODULE_ROOT}" check-ignore -q "${REL}"; then die "${REL}/ 未被 .gitignore 忽略——在此安装依赖会把 site-packages 灌进版本库(QC #1)。 修法:在 ${MODULE_ROOT}/.gitignore 增加一行「${REL}/」, 若历史已入库再执行「git rm -r --cached ${REL}」(保留本地文件), 然后重跑本脚本自证:git check-ignore -v ${REL}" fi TRACKED="$(git -C "${MODULE_ROOT}" ls-files "${REL}" | wc -l | tr -d ' ')" if [ "${TRACKED}" != "0" ]; then die "${REL}/ 已被 .gitignore 忽略但仍有 ${TRACKED} 个文件在版本跟踪中(历史入库)。 执行「git rm -r --cached ${REL}」清出跟踪后重跑本脚本。" fi log "仓库卫生门禁通过:${REL}/ 已被 .gitignore 忽略且未被版本跟踪" fi if [ ! -x "${VENV}/bin/python" ]; then log "创建测试 venv: ${VENV}" "${HOST_PY}" -m venv "${VENV}" || die "python3 -m venv 失败(宿主缺 venv 模块?)" else log "复用已存在的测试 venv: ${VENV}" fi VPY="${VENV}/bin/python" # 升级 pip 属尽力而为(离线/内网源可能不可达),失败不阻断——真正的门禁是下一步安装 "${VPY}" -m pip install --quiet --upgrade pip >/dev/null 2>&1 || log "pip 自升级跳过(不影响后续安装)" log "安装测试依赖: ${REQ_DEV}" "${VPY}" -m pip install -r "${REQ_DEV}" || die "安装 pytest 失败:检查内网 PyPI 源可达性(见 README「Running tests」排障)" if [ "${PBL_EVIDENCE_INSTALL_PKG:-0}" = "1" ]; then log "安装模块本体(可编辑):pip install -e ${MODULE_ROOT}" "${VPY}" -m pip install -e "${MODULE_ROOT}" || die "pip install -e . 失败" fi log "自证可用:$("${VPY}" -m pytest --version 2>&1 | head -1)" log "TEST ENV READY: ${VPY}" log "直接跑测试(二选一,均不需要手工注入 PYTHONPATH):" log " bash ${MODULE_ROOT}/scripts/run_tests.sh" log " source ${MODULE_ROOT}/scripts/activate_test_env.sh && python3 -m pytest tests/test_m5a_idempotency.py -v"