From 8a31fb0126df3cf79630c71e2f99ee8bff029443 Mon Sep 17 00:00:00 2001 From: hemingway Date: Mon, 31 Aug 2026 19:25:14 -0700 Subject: [PATCH] ci: run the Python tests on the release path (sable-cazq) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python/tests/ executed in exactly one workflow in this repo — test-comprehensive.yml — and that workflow is not on the release path. publish.yaml had no Python test job at all, and validate-release.yml's test-build ran `python -m build` without pytest, so the pre-release gate asserted the Python package compiles and never that it works. A Python-only regression reached PyPI green, and PyPI is not somewhere you can quietly unship from. - publish.yaml: add test-python, mirroring test-comprehensive.yml's job. It runs in parallel with test-node, so it costs no wall clock the release was not already spending. - publish.yaml: gate BOTH publish jobs on it. The two registries publish from one push and version parity is enforced elsewhere, so a suite that goes red after npm has already published leaves the two runtimes at different versions on the two indexes. The divergence is the failure mode whichever half breaks. - publish.yaml: give test-node a Python toolchain. The node release path looked safe because publish-node has a `needs:` clause, but the job it needed was quietly weaker than the same job in test-comprehensive.yml: tests/cross-runtime-parity.test.ts gates its whole describe block on `python3 -c "import typer"` and describe.skip is silent, so all 40 parity assertions were skipped and the suite reported green. Verified by stubbing python3: "40 skipped", exit 0. Those tests are what enforce the dual-implementation contract — the ones a release least wants to skip. - validate-release.yml: run pytest in test-build. ~80s. Note for PR #221, which touches the same job: it adds `needs: [test-node, test-package]` to publish-python to stop a red NODE suite shipping the PyPI package. That is a different hole. Keep both sets of needs if the two land together. --- .github/workflows/publish.yaml | 61 +++++++++++++++++++++++++- .github/workflows/validate-release.yml | 9 +++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 176b189..01459c8 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -26,15 +26,64 @@ jobs: with: node-version: "20" + # sable-cazq — tests/cross-runtime-parity.test.ts gates its whole + # describe block on `python3 -c "import typer"` succeeding, and + # describe.skip is silent: with no Python here the release path ran + # `pnpm test`, skipped all 40 parity assertions and reported green. + # Those are the tests that enforce the dual-implementation contract, + # so they are the ones a release least wants to skip. Mirrors the + # setup in test-comprehensive.yml's test-node. + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Enable pnpm run: corepack enable && corepack prepare pnpm@10 --activate - name: Install dependencies run: pnpm install --frozen-lockfile + - name: Install Python dependencies (for cross-runtime parity tests) + working-directory: ./python + run: | + pip install -e ".[dev]" 2>/dev/null || pip install -e . + - name: Run tests run: pnpm test + # sable-cazq — python/tests/ ran in exactly one place in this repo + # (test-comprehensive.yml) and it was not the release path. publish.yaml + # had no Python test job at all and validate-release.yml only built the + # wheel, so a Python-only regression reached PyPI green — and PyPI is not + # somewhere you can quietly unship from. Mirrors test-comprehensive.yml's + # test-python; runs in parallel with test-node, so it costs no wall clock + # the release was not already spending. + test-python: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./python + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + # pyproject.toml is Poetry-style: dev deps live under + # [tool.poetry.group.dev.dependencies], which is not a PEP 621 extra, + # so `.[dev]` always falls through to the bare install. The explicit + # pytest line is what actually provides the test deps. + - name: Install dependencies + run: | + pip install -e ".[dev]" 2>/dev/null || pip install -e . + pip install pytest pytest-mock pytest-asyncio + + - name: Run all tests + run: python -m pytest tests/ -v + env: + RAFTER_API_KEY: ${{ secrets.RAFTER_API_KEY }} + test-package: runs-on: ubuntu-latest defaults: @@ -82,7 +131,12 @@ jobs: echo "OK: pre-commit hook installed end-to-end" publish-node: - needs: [test-node, test-package] + # test-python is a gate on the npm release too, not only the PyPI one. + # The two registries are published from one push and version parity is + # enforced, so a Python suite that fails after npm has already published + # leaves the two runtimes at different versions on the two indexes — + # the divergence is the failure mode, whichever half breaks. (sable-cazq) + needs: [test-node, test-python, test-package] runs-on: ubuntu-latest # Trusted Publishing requires id-token: write in scope for THIS job (the # top-level grant covers it, but documented here for the job-local audit @@ -143,6 +197,11 @@ jobs: run: npm publish --access public --provenance publish-python: + # sable-cazq. PR #221 adds `needs: [test-node, test-package]` here for a + # different reason — it stops a red NODE suite shipping the PyPI package. + # This adds test-python, which is what makes the Python tests run on the + # release path at all. Keep both if the two land together. + needs: [test-node, test-python, test-package] runs-on: ubuntu-latest defaults: run: diff --git a/.github/workflows/validate-release.yml b/.github/workflows/validate-release.yml index a8b459c..96e4037 100644 --- a/.github/workflows/validate-release.yml +++ b/.github/workflows/validate-release.yml @@ -93,11 +93,18 @@ jobs: pnpm run build pnpm test - - name: Build Python package + # sable-cazq — this job ran `pnpm test` for Node and nothing but a + # wheel build for Python, so the pre-release gate asserted that the + # Python package *compiles*, never that it works. ~80s of pytest is + # the difference between those two claims. + - name: Build and test Python package run: | cd python python -m pip install --upgrade build python -m build + pip install -e . + pip install pytest pytest-mock pytest-asyncio + python -m pytest tests/ -q - name: Verify all artifacts run: |