From 8a324b9f783c7688ce2daee57d7e025eb9f3837a Mon Sep 17 00:00:00 2001 From: rp-maintainers Date: Fri, 7 Aug 2026 10:00:30 +0000 Subject: [PATCH] ci: run the test suite on the release path, and pin the actions `publish.yml` built a wheel and pushed it to PyPI on any `v*` tag without running a single assertion. `ruff`, `mypy` and the 3.9-3.13 `pytest` matrix all existed and all passed -- on push/PR to `main`, which the tag path never touches. A tag could be cut from any commit and shipped to users untested. Gate the release on CI by CALLING it (`workflow_call`) rather than restating the steps. A second copy of the matrix would be one more thing to keep in sync, and that drift stays invisible until a bad release is already public. Also pin every action to a commit SHA. This is the workspace convention and it matters most here: these four actions run on the path that produces and uploads a public artifact, so a moved tag upstream is a supply-chain event. Pinned at the current majors -- this is not a version bump. Note `pypa/gh-action-pypi-publish` v1.14.2 is an annotated tag, so its commit SHA differs from the SHA its ref points at; the commit is what is pinned. --- .github/workflows/ci.yml | 5 +++++ .github/workflows/publish.yml | 19 ++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0a803f3..13b26de 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,11 @@ on: branches: [main] pull_request: branches: [main] + # Callable so the RELEASE path runs these exact assertions rather than a copy + # of them. publish.yml gates on this workflow; a second definition would be + # one more thing to keep in sync, and the drift would be invisible until a + # bad release was already on PyPI. + workflow_call: concurrency: group: ci-${{ github.ref }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 0dbe881..ac3b1c9 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -8,16 +8,25 @@ permissions: contents: read jobs: + # The release path runs the SAME assertions as CI, by calling CI — not by + # restating them. Until this job existed, `ruff`, `mypy` and the 3.9-3.13 + # pytest matrix all ran on push/PR to main and none of them ran on the tag + # that actually publishes to PyPI: a `v*` tag could be cut from any commit and + # shipped to users without a single test executing. + test: + uses: ./.github/workflows/ci.yml + build: + needs: test runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v5 + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: '3.12' - run: pip install build - run: python -m build - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: dist path: dist/ @@ -29,12 +38,12 @@ jobs: permissions: id-token: write # trusted publishing (OIDC) — no token secret needed steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: dist path: dist/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2 # Trusted publishing: configure the PyPI project to trust # routeplane-core/routeplane-python via the "publish" workflow + # "pypi" environment. No PYPI_TOKEN secret required.