test: the runner's python3 cannot make a venv — fall back to pip --target
Second CI run, second real finding. `python3 -m venv` fails outright on the shared box: Debian splits ensurepip into python3-venv, which is not installed, so there is no venv to be had there at all. `make oracles` now tries venv, then `pip install --target .oracles`, and test/oracle.mjs puts that directory on PYTHONPATH when it exists. Both give the oracles their imports without touching the box, which is the constraint that matters: its user-data is fixed at launch, so anything needing a change there is a launch-template version and an instance replacement. If neither works it now prints what it actually found — the python3 version and the exact venv and pip errors — rather than failing with one of them and leaving the other unknown. That is the whole reason this took two runs instead of one. Both paths verified locally: venv, and pip --target with .venv absent.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
3 files changed,
+42
−4
.gitignore+1 −0Makefile+27 −4test/oracle.mjs+14 −0
CI failed — run details
CI log
=== vet === go vet ./... === fmt === === test === go test ./... ? github.com/carlosframework/contra/cmd/contra [no test files] ? github.com/carlosframework/contra/internal/contra [no test files] ? github.com/carlosframework/contra/internal/contra/auth [no test files] ok github.com/carlosframework/contra/internal/contra/crypto (cached) ? github.com/carlosframework/contra/internal/contra/icons [no test files] ? github.com/carlosframework/contra/internal/contra/mail [no test files] ? github.com/carlosframework/contra/internal/contra/model [no test files] ? github.com/carlosframework/contra/internal/contra/store [no test files] ? github.com/carlosframework/contra/internal/contra/vault [no test files] ok github.com/carlosframework/contra/internal/contratest (cached) === js-test === make oracles: this python3 can neither create a venv nor run pip. python3: Python 3.12.3 venv : pip : /usr/bin/python3: No module named pip Install python3-venv or python3-pip, or set CONTRA_PYTHON to an interpreter that already has test/requirements.txt. make: *** [Makefile:120: oracles] Error 1 amadan: step js-test failed: exit status 2 amadan: job failed at step js-test — its output is above, not at the tail of this log
diff --git a/.gitignore b/.gitignore| index 3868e97..922e7ba 100644 |
| --- a/.gitignore |
| +++ b/.gitignore |
| @@ -14,3 +14,4 @@ |
| # The oracle venv, built by `make oracles`. Pinned by test/requirements.txt, |
| # so it is reproducible rather than worth committing. |
| /.venv/ |
| +/.oracles/ |
diff --git a/Makefile b/Makefile| index ead136b..c8053bc 100644 |
| --- a/Makefile |
| +++ b/Makefile |
| @@ -109,13 +109,36 @@ test-js: |
| # |
| # Pinned in test/requirements.txt, because an oracle that drifts is an oracle |
| # that stops meaning what it said last week. |
| +# Two ways in, because a Debian python3 without the python3-venv package |
| +# cannot create a venv at all (no ensurepip) — which is exactly what the |
| +# shared runner turned out to be. A venv is preferred where it works; where |
| +# it does not, the packages go into .oracles/ and test/oracle.mjs puts that |
| +# on PYTHONPATH. Both give the oracles their imports without touching the |
| +# box, which is the requirement: its user-data is fixed at launch. |
| oracles: |
| @command -v python3 >/dev/null 2>&1 || { \ |
| echo "make oracles: python3 is not installed."; exit 1; } |
| - python3 -m venv .venv |
| - .venv/bin/pip install --quiet --upgrade pip |
| - .venv/bin/pip install --quiet -r test/requirements.txt |
| - @echo "oracles ready: .venv/bin/python3" |
| + @set -e; \ |
| + if python3 -m venv .venv >/dev/null 2>&1; then \ |
| + .venv/bin/pip install --quiet --upgrade pip; \ |
| + .venv/bin/pip install --quiet -r test/requirements.txt; \ |
| + echo "oracles ready: .venv/bin/python3"; \ |
| + elif python3 -m pip --version >/dev/null 2>&1; then \ |
| + rm -rf .venv; \ |
| + python3 -m pip install --quiet --target .oracles \ |
| + -r test/requirements.txt; \ |
| + echo "oracles ready: python3 with PYTHONPATH=.oracles"; \ |
| + else \ |
| + rm -rf .venv; \ |
| + echo "make oracles: this python3 can neither create a venv nor run pip."; \ |
| + echo " python3: $$(python3 --version 2>&1)"; \ |
| + echo " venv : $$(python3 -m venv /tmp/contra-venv-probe 2>&1 | tail -1)"; \ |
| + echo " pip : $$(python3 -m pip --version 2>&1 | tail -1)"; \ |
| + rm -rf /tmp/contra-venv-probe; \ |
| + echo " Install python3-venv or python3-pip, or set CONTRA_PYTHON to"; \ |
| + echo " an interpreter that already has test/requirements.txt."; \ |
| + exit 1; \ |
| + fi |
| # The browser crypto in WebKit, which is the engine this product can least |
| # afford to be wrong about and the one the Chromium rig never touches. |
diff --git a/test/oracle.mjs b/test/oracle.mjs| index 6f7426e..a91cd19 100644 |
| --- a/test/oracle.mjs |
| +++ b/test/oracle.mjs |
| @@ -31,6 +31,19 @@ export function oraclePython() { |
| return "python3"; |
| } |
| +// oracleEnv adds .oracles/ to PYTHONPATH when `make oracles` had to fall back |
| +// to `pip --target` — which it does on a python3 with no ensurepip, where a |
| +// venv cannot be created at all. Harmless when the directory is absent. |
| +function oracleEnv() { |
| + const target = join(repo, ".oracles"); |
| + if (!existsSync(target)) return process.env; |
| + const existing = process.env.PYTHONPATH; |
| + return { |
| + ...process.env, |
| + PYTHONPATH: existing ? `${target}:${existing}` : target, |
| + }; |
| +} |
| + |
| // run executes one oracle and returns its stdout. |
| // |
| // It does NOT skip when the packages are missing. A skipped oracle is |
| @@ -44,6 +57,7 @@ export function run(script, input) { |
| return execFileSync(python, [join(here, script)], { |
| input: JSON.stringify(input), |
| maxBuffer: 64 << 20, |
| + env: oracleEnv(), |
| }).toString(); |
| } catch (err) { |
| const text = String(err.stderr || err.message || ""); |