deliver: 交付收口(引擎代为提交)

This commit is contained in:
agent.develop 2026-09-22 21:50:14 +08:00
parent 58fdd5456c
commit 262e87fc36
4 changed files with 113 additions and 7 deletions

10
.gitignore vendored
View File

@ -10,17 +10,15 @@ dist/
build/
.venv/
venv/
# test virtualenv created by scripts/setup_test_env.sh (DISC.2). Tracked by mistake
# in commit 0a87e13 (1171 site-packages files); removed from the index with
# `git rm -r --cached .venv-test`. This line is what makes that claim true.
# Test virtualenv created by scripts/setup_test_env.sh (发现项 DISC.2).
# It was mistakenly committed in 0a87e13 (1171 site-packages files tracked);
# removed from the index with `git rm -r --cached .venv-test` (local files kept).
# Self-prove the exclusion is live: git check-ignore -v .venv-test
.venv-test/
# one-off patch / scratch tooling dir: never commit (repo hygiene, QC#11)
.scratch_m5a/
# local test-run logs written by scripts/run_tests.sh (not deliverables)
logs/
# CRUD build output (generated by xls2ui from json/*.json; read-only, not tracked;
# listed per alias, no wildcards). Host apps/pbls/build.sh mounts these as symlinks
# under wwwroot/{alias}, so the generated dirs must not live in the module repo.

View File

@ -0,0 +1,60 @@
#!/usr/bin/env bash
# =====================================================================
# pbl_evidence 测试环境「一行激活」(发现项 DISC.2)
#
# 目的:让下面这条命令在**不手工注入 PYTHONPATH** 的前提下可直接执行:
# source scripts/activate_test_env.sh
# python3 -m pytest tests/test_m5a_idempotency.py -v # 8 passed, 1 skipped
#
# 做了什么(只做两件事,不改任何环境变量以外的东西):
# ① 若宿主 python3 已能 import pytest(正规长期方案:宿主 pip 装了
# requirements-dev.txt / `pip install -e .[test]`)→ 什么都不做,直接返回;
# ② 否则 source 模块测试 venv(默认 .venv-test,缺则先调
# scripts/setup_test_env.sh 引导),使 PATH 里的 `python3` 就是 venv 解释器。
#
# 为什么不是「往 PYTHONPATH 里塞 site-packages」:那正是 DISC.2 的根因——把
# 工作空间私有临时目录(.pylibs)伪装成可用环境,换机器/换角色即失效,且
# 污染宿主其它项目的依赖解析。venv 激活是标准、显式、可复现的做法。
#
# 注意:必须 `source`(或 `. `)执行;用 `bash scripts/activate_test_env.sh`
# 子进程执行只会影响子 shell,退出即失效(脚本会检测并提示)。
# =====================================================================
# 被 source 时 BASH_SOURCE[0] 与 $0 不同;被直接执行时相同(此时激活无意义)
_AEV_SELF="${BASH_SOURCE[0]:-$0}"
_AEV_SOURCED=1
if [ "${_AEV_SELF}" = "${0}" ]; then
_AEV_SOURCED=0
fi
_AEV_MODULE_ROOT="$(cd "$(dirname "${_AEV_SELF}")/.." && pwd)"
_AEV_VENV="${PBL_EVIDENCE_TEST_VENV:-${_AEV_MODULE_ROOT}/.venv-test}"
_aev_host_has_pytest() {
local _py
_py="$(command -v python3 || true)"
[ -n "${_py}" ] || return 1
"${_py}" -c 'import pytest' >/dev/null 2>&1
}
if _aev_host_has_pytest; then
echo "[activate_test_env] 宿主 python3 已有 pytest($(python3 -m pytest --version 2>&1 | head -1)),无需激活 venv"
return 0 2>/dev/null || exit 0
fi
if [ ! -x "${_AEV_VENV}/bin/activate" ]; then
echo "[activate_test_env] 测试 venv 不存在:${_AEV_VENV}"
echo "[activate_test_env] 先引导:bash ${_AEV_MODULE_ROOT}/scripts/setup_test_env.sh"
if [ "${_AEV_SOURCED}" = "1" ]; then return 1; else exit 1; fi
fi
# shellcheck disable=SC1091
. "${_AEV_VENV}/bin/activate"
echo "[activate_test_env] 已激活测试 venv: ${_AEV_VENV}"
echo "[activate_test_env] python3 = $(command -v python3) ($(python3 -V 2>&1))"
echo "[activate_test_env] pytest = $(python3 -m pytest --version 2>&1 | head -1)"
if [ "${_AEV_SOURCED}" != "1" ]; then
echo "[activate_test_env][WARN] 本脚本被当作子进程执行,激活随子 shell 结束而失效。请改用:source ${_AEV_SELF}"
exit 0
fi
return 0

View File

@ -9,6 +9,9 @@
# PBL_EVIDENCE_TEST_DB=1 bash scripts/run_tests.sh # 打开真实双次采集用例(需测试库)
# PBL_EVIDENCE_TEST_DB=1 PBL_EVIDENCE_TEST_TENANT=m5a-demo bash scripts/run_tests.sh
#
# 想在当前 shell 里**原样**敲 `python3 -m pytest ...`(任务要求的字面命令):
# source scripts/activate_test_env.sh && python3 -m pytest tests/test_m5a_idempotency.py -v
#
# 解释器解析顺序(第一个「能 import pytest」的胜出):
# ① $PBL_EVIDENCE_TEST_PYTHON —— 显式指定(CI 用宿主 venv 时走这条)
# ② 宿主 python3 —— 若已 pip install pytest / -e .[test](正规长期方案)
@ -35,6 +38,15 @@ has_pytest() {
cd "${MODULE_ROOT}"
# ---- 仓库卫生自检(QC #1 回归防线,不阻断测试,只打印)----
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
_hyg="$(git check-ignore -q "${VENV#${MODULE_ROOT}/}" && echo ignored || echo NOT-ignored)"
_trk="$(git ls-files "${VENV#${MODULE_ROOT}/}" | wc -l | tr -d ' ')"
log "仓库卫生: .gitignore=${_hyg} tracked_in_venv=${_trk}(期望 ignored / 0)"
[ "${_hyg}" = "ignored" ] || log "[WARN] venv 落点未被 .gitignore 忽略,会把依赖目录提交进版本库"
[ "${_trk}" = "0" ] || log "[WARN] venv 内有 ${_trk} 个文件仍被版本跟踪,执行 git rm -r --cached '${VENV#${MODULE_ROOT}/}'"
fi
# ---- 解析解释器 ----
PY=""
for cand in "${PBL_EVIDENCE_TEST_PYTHON:-}" "$(command -v python3 || true)" "${VENV}/bin/python"; do

View File

@ -8,14 +8,27 @@
# 不可复现、且会让 test 角色误判为「用例失败/环境不可用」。
#
# 本脚本把「测试前置依赖」变成显式、可重放的步骤:
# 1) 在模块仓库内建独立测试 venv(默认 .venv-test,已在 .gitignore 排除)
# 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 反复调用。
# =====================================================================
@ -32,6 +45,26 @@ 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 模块?)"
@ -52,3 +85,6 @@ 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"