From cc0151b7ac5cd40de145bd07fafed8e5770d742c Mon Sep 17 00:00:00 2001 From: Marcus Baw Date: Tue, 16 Jun 2026 16:23:33 +0100 Subject: [PATCH] Fix test-dependency parity between PR CI and PyPI publish The v4.5.0 PyPI publish failed because pandas was not installed when the publish workflow ran the test suite, while the PR/push workflow happened to pull pandas in transitively via the 'notebook' extra. The two workflows therefore tested different environments and the gap was only caught at release time. Root cause: test dependencies were declared in two places that drifted. pandas is a genuine test-only dependency (only rcpchgrowth/tests/test_who.py imports it; nothing in the library does). Changes: - Add a [dev] optional-dependencies group in pyproject.toml as the single source of truth for test/release tooling (pytest, pandas, bump2version). - requirements.txt now resolves to '-e .[dev]' so local, PR CI and publish all install the same set. - Make the pytest workflow reusable via workflow_call. - The publish workflow now reuses that exact test job (jobs.test.uses:) with deploy gated on 'needs: test', instead of duplicating install + pytest steps. A release can no longer be published against an environment the PR matrix did not test. - Remove the unused 'import pandas' from rcpchgrowth/who.py. Validated: clean-room '.[dev]' install provides pandas and test_who.py passes 995/995; full local suite passes 145018 / 1700 skipped. Note: the test job is renamed from 'deploy' to 'test'. Branch-protection required status checks (if/when enabled) should reference 'test (3.x)'. --- .github/workflows/python-publish.yml | 23 ++++++------------- .../run-pytest-on-push-and-all-prs.yml | 9 +++++--- pyproject.toml | 9 ++++++++ rcpchgrowth/who.py | 1 - requirements.txt | 17 +++++++++----- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 73e14d4..0547a5f 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -20,7 +20,13 @@ on: types: [created] jobs: + # Reuse the EXACT same test job that runs on every PR and push, so a release + # can never be published with a dependency/config the PR matrix did not test. + test: + uses: ./.github/workflows/run-pytest-on-push-and-all-prs.yml + deploy: + needs: test runs-on: ubuntu-latest steps: @@ -31,25 +37,10 @@ jobs: with: python-version: "3.13" - - name: Cache pip - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('pyproject.toml', 'requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install build & test dependencies + - name: Install build dependencies run: | python -m pip install --upgrade pip pip install build twine - # Install test/dev dependencies (pytest etc.) - pip install -r requirements.txt - # Install the project (will pull runtime deps from pyproject) - pip install . - - - name: Run pytest - run: pytest -q - name: Build (sdist & wheel) run: python -m build diff --git a/.github/workflows/run-pytest-on-push-and-all-prs.yml b/.github/workflows/run-pytest-on-push-and-all-prs.yml index 886abcf..44607f1 100644 --- a/.github/workflows/run-pytest-on-push-and-all-prs.yml +++ b/.github/workflows/run-pytest-on-push-and-all-prs.yml @@ -13,6 +13,10 @@ name: Run Pytest on pushes to main branches or PRs to any branch # https://github.com/mheap/pin-github-action on: + # Allow this exact test job to be reused by other workflows (e.g. the PyPI + # publish workflow) so that a release is gated on the SAME tests that run on + # every PR and push - no duplicated, drift-prone test steps. + workflow_call: pull_request: branches: - "*" @@ -23,7 +27,7 @@ on: - live jobs: - deploy: + test: runs-on: ubuntu-latest # Runs tests on multiple python versions across the range we support strategy: @@ -49,8 +53,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install .[notebook] # ensure optional extras still work - pip install -r requirements.txt + pip install .[dev] - name: Run pytest run: pytest -q diff --git a/pyproject.toml b/pyproject.toml index 05f770c..25c8805 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,15 @@ notebook = [ "jupyterlab", "ipykernel", ] +# Everything needed to run the test suite and release tooling. This is the +# SINGLE source of truth for test dependencies: every environment (local, +# PR CI, and the PyPI publish workflow) installs `.[dev]` so they cannot +# drift apart. `pandas` is required by rcpchgrowth/tests/test_who.py. +dev = [ + "pytest", + "pandas>=1.5", + "bump2version", +] [tool.setuptools] include-package-data = true diff --git a/rcpchgrowth/who.py b/rcpchgrowth/who.py index fb7913c..285a6f8 100644 --- a/rcpchgrowth/who.py +++ b/rcpchgrowth/who.py @@ -6,7 +6,6 @@ import json from importlib import resources from pathlib import Path -import pandas as pd # rcpch imports from .constants import * diff --git a/requirements.txt b/requirements.txt index 364566d..62b05ef 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,11 @@ -bump2version -pytest - -# Runtime dependencies (python-dateutil, scipy, matplotlib) are declared in pyproject.toml -# and installed automatically when installing the package. setuptools is provided -# via the build-system section, so it is not needed here. +# Local development / CI convenience installer. +# +# Installs the package in editable mode plus the test/dev tooling declared in +# the [dev] optional-dependencies group in pyproject.toml. Keeping the actual +# dependency list in pyproject.toml means there is a SINGLE source of truth +# shared by every environment (local, PR CI, and the PyPI publish workflow), +# so test dependencies can never drift between them. +# +# Runtime dependencies (python-dateutil, scipy) are declared in +# pyproject.toml [project.dependencies] and installed automatically. +-e .[dev]