fix: isolate Gitea Python dependencies
Staging Backend Quality Gate (push the reviewed main SHA to staging to auto-deploy) / validate (push) Successful in 20m16s
Staging Backend Quality Gate (push the reviewed main SHA to staging to auto-deploy) / publish-and-deploy (push) Failing after 5m23s

Run Python tooling in repository virtual environments so xiaoxin respects the PEP 668 system package boundary.
This commit is contained in:
linmeng
2026-07-27 21:23:29 +08:00
parent 1b8d6fcce6
commit f711e77849
7 changed files with 98 additions and 25 deletions
+10 -7
View File
@@ -45,8 +45,10 @@ jobs:
- name: Install dependencies
run: |
set -euo pipefail
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt -r requirements-dev.txt
python3 -m venv .venv
export PATH="$PWD/.venv/bin:$PATH"
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
npm ci --prefix frontend
- name: Validate backend, package, frontend, and database contracts
env:
@@ -54,11 +56,12 @@ jobs:
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
run: |
set -euo pipefail
ruff check scripts/run_quality_gate.py tests/test_varga_bphs.py tests/test_ashtakavarga_invariants.py tests/test_cli_smoke.py tests/test_yoga_rules_integrity.py
python3 -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
python3 scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime
python3 scripts/commercial_privacy_artifact_scan.py --json
python3 -m build
export PATH="$PWD/.venv/bin:$PATH"
.venv/bin/ruff check scripts/run_quality_gate.py tests/test_varga_bphs.py tests/test_ashtakavarga_invariants.py tests/test_cli_smoke.py tests/test_yoga_rules_integrity.py
.venv/bin/python -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
.venv/bin/python scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime
.venv/bin/python scripts/commercial_privacy_artifact_scan.py --json
.venv/bin/python -m build
npm test --prefix frontend
npm run lint --prefix frontend
npm run build --prefix frontend
+8 -6
View File
@@ -31,14 +31,16 @@ jobs:
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
run: |
set -euo pipefail
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt -r requirements-dev.txt
python3 -m venv .venv
export PATH="$PWD/.venv/bin:$PATH"
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
npm ci --prefix frontend
ruff check scripts/run_quality_gate.py tests/test_varga_bphs.py tests/test_ashtakavarga_invariants.py tests/test_cli_smoke.py tests/test_yoga_rules_integrity.py
python3 -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
python3 scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime
python3 scripts/commercial_privacy_artifact_scan.py --json
python -m py_compile scripts/*.py jyotish_vedic/*.py mcp_server.py
python scripts/run_quality_gate.py --profile quick --skip-yoga-logic --skip-frontend-runtime
python scripts/commercial_privacy_artifact_scan.py --json
npm test --prefix frontend
npm run lint --prefix frontend
npm run build --prefix frontend
python3 -m build
python -m build
+10 -4
View File
@@ -27,11 +27,17 @@ jobs:
- name: Build and check package
run: |
set -euo pipefail
python3 -m pip install build twine
python3 -m build
python3 -m twine check dist/*
python3 -m venv .venv
export PATH="$PWD/.venv/bin:$PATH"
python -m pip install --upgrade pip
python -m pip install build twine
python -m build
python -m twine check dist/*
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: python3 -m twine upload --skip-existing dist/*
run: |
set -euo pipefail
export PATH="$PWD/.venv/bin:$PATH"
python -m twine upload --skip-existing dist/*
+6 -4
View File
@@ -31,8 +31,10 @@ jobs:
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
run: |
set -euo pipefail
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt -r requirements-dev.txt playwright
python3 -m playwright install --with-deps chromium
python3 -m venv .venv
export PATH="$PWD/.venv/bin:$PATH"
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt playwright
python -m playwright install --with-deps chromium
npm ci --prefix frontend
python3 scripts/run_quality_gate.py --profile release
python scripts/run_quality_gate.py --profile release
+6 -4
View File
@@ -30,11 +30,13 @@ jobs:
NEXT_PUBLIC_SUPABASE_ANON_KEY: ci-placeholder
run: |
set -euo pipefail
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt -r requirements-dev.txt
python3 -m venv .venv
export PATH="$PWD/.venv/bin:$PATH"
python -m pip install --upgrade pip
python -m pip install -r requirements.txt -r requirements-dev.txt
npm ci --prefix frontend
python3 -m pytest -vv --maxfail=1
python3 tests/run_all.py
python -m pytest -vv --maxfail=1
python tests/run_all.py
npm test --prefix frontend
npm run lint --prefix frontend
npm run build --prefix frontend
+16
View File
@@ -1519,3 +1519,19 @@
- 相关记录:无
- 复发自:无
- 修复版本:待提交(本地可测)
## BUG-083 | Gitea xiaoxin Runner 向系统 Python 安装依赖触发 PEP 668
- 状态:resolved
- 首次发现:2026-07-27
- 最近更新:2026-07-27
- 影响面:Gitea 后端质量门禁、CI、完整测试、发布质量门禁与 PyPI 发布
- 用户现象:xiaoxin Runner 执行 `python3 -m pip install` 时以 `externally-managed-environment` 失败,工作流无法进入后续 Ruff、pytest、Playwright、构建或上传阶段。
- 触发条件:基于启用 PEP 668 的系统 Python 运行任一包含 pip 安装的 `.gitea/workflows/*.yml`
- 根因:BUG-082 统一 Gitea Runner 与原生工具链时只审计了 runner、checkout、触发器和镜像主链,没有约束 Python 依赖隔离;五个工作流仍直接向 Runner 的系统 Python 执行 pip 安装,且 Gitea step 之间不会自动延续 shell 内的 PATH 修改。
- 修复:所有包含 pip 安装的 Gitea workflow 都先创建仓库内 `.venv` 并在同一安装 step 显式将其 `bin` 目录置于 PATH;后续独立 Python step(包括后端验证与 PyPI 上传)再次显式设置同一 PATH。未使用 `--break-system-packages`,也未修改 GitHub workflows。
- 验证:前端 Gitea workflow 契约测试枚举全部 `.gitea/workflows/*.yml`,确认五个 pip workflow 均创建并选择 `.venv`、后续 Python step 重新选择 `.venv`、不存在裸 `pip install``--break-system-packages`;全部 Gitea YAML 完成解析验证。
- 防复发:工作流审计必须动态枚举全部 Gitea YAML;任何出现 pip install 的 workflow 都必须创建 `.venv`,任何随后执行 Python、Ruff、pytest、Playwright 或构建/上传命令的独立 step 都必须显式导出 venv PATH。
- 相关记录:BUG-082
- 复发自:BUG-082
- 修复版本:待提交(本地可测)
@@ -420,6 +420,48 @@ test("all Gitea workflows use xiaoxin, native checkout, and safe triggers", () =
assert.doesNotMatch(all, /GITEA_REGISTRY_USERNAME|GITEA_REGISTRY_TOKEN|git\.copse\.top\/root\/jyotisha-(?:api|web)/);
});
test("Gitea workflows isolate pip installs and Python tooling in a virtualenv", () => {
const names = readdirSync(giteaWorkflowDirectory)
.filter((name) => name.endsWith(".yml"));
const pipInstall = /(?:python3?\s+-m\s+)?pip3?\s+install/;
const pythonTooling = /^\s+(?:python3?|ruff|pytest|playwright|build)(?:\s|$)/m;
const expected = [
"backend-quality-gate.yml",
"ci.yml",
"publish-pypi.yml",
"release-quality-gate.yml",
"test.yml",
];
const workflowsWithPip: string[] = [];
for (const name of names) {
const workflow = read(new URL(name, giteaWorkflowDirectory));
if (!pipInstall.test(workflow)) continue;
workflowsWithPip.push(name);
assert.doesNotMatch(workflow, /--break-system-packages/, name);
assert.doesNotMatch(workflow, /^\s+pip3?\s+install/m, name);
const runBlocks = workflow.match(/^\s{8}run:\s*(?:\|\s*\n(?:\s{10}.*(?:\n|$))+|[^\n]+)$/gm) ?? [];
let virtualenvCreated = false;
for (const block of runBlocks) {
const installsWithPip = pipInstall.test(block);
if (installsWithPip) {
assert.match(block, /python3 -m venv \.venv/, `${name}: pip step must create .venv`);
virtualenvCreated = true;
}
if (virtualenvCreated && pythonTooling.test(block)) {
assert.match(
block,
/export PATH="\$PWD\/\.venv\/bin:\$PATH"/,
`${name}: every later Python step must select .venv`,
);
}
}
}
assert.deepEqual(workflowsWithPip.sort(), expected);
});
test("Gitea staging push validates once then publishes and deploys immutable ACR images", () => {
const workflow = read(giteaQualityWorkflow);
assert.match(workflow, /publish-and-deploy:[\s\S]*needs: validate/);