Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 32 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,16 @@
on:
pull_request:
branches: [main]
# #413 — docs-only PRs skip pytest. paths-ignore is a UNION filter:
# the workflow runs unless every changed file matches an ignored path.
# A mixed PR (e.g. src/foo.py + docs/x.md) still runs full CI.
# TODO(post-public-flip): once branch protection requires this check,
# migrate to a dorny/paths-filter detection job so the workflow always
# runs and reports a status, with downstream jobs gated by `if:`.
paths-ignore:
- 'docs/**'
- '**/*.md'
- 'LICENSE'
- '.github/ISSUE_TEMPLATE/**'
- '.github/PULL_REQUEST_TEMPLATE.md'
# Workflow runs on every PR so branch protection can rely on pytest
# checks reporting on every PR. The detect job uses dorny/paths-filter
# to short-circuit pytest when changes don't touch Python source/tests/
# deps; pytest is `skipped` (which branch protection accepts as
# satisfied) rather than `expected` / unregistered.
#
# Migration rationale (#413 → #426 lesson): paths-ignore caused branch
# protection to mark pytest as `expected` for docs-only PRs, blocking
# merge. The detection-job pattern always registers the check; the
# `if:` on pytest gates the WORK, not the registration.

permissions:
contents: read
Expand All @@ -24,7 +22,29 @@
cancel-in-progress: true

jobs:
detect:
runs-on: ubuntu-latest
outputs:
python: ${{ steps.filter.outputs.python }}
Comment on lines +27 to +28

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Guard against missing or malformed paths-filter output to avoid surprising job skips.

This relies on steps.filter.outputs.python always being set to 'true' or 'false'. If the dorny/paths-filter step is renamed, skipped, or fails, needs.detect.outputs.python becomes empty and pytest will never run. Consider normalizing the value through an intermediate step/job-level output that defaults to 'true' when unset, so misconfigurations still run pytest rather than silently skipping it.

Suggested implementation:

  detect:
    runs-on: ubuntu-latest
    outputs:
      python: ${{ steps.normalize-python.outputs.python }}

      - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
        id: filter
        with:
          filters: |
            python:
              - 'src/**'
              - 'tests/**'
      - name: Normalize python filter output
        id: normalize-python
        env:
          PYTHON_MATCH: ${{ steps.filter.outputs.python }}
        run: |
          if [ "${PYTHON_MATCH}" = "true" ] || [ "${PYTHON_MATCH}" = "false" ]; then
            echo "python=${PYTHON_MATCH}" >> "$GITHUB_OUTPUT"
          else
            # Default to true if the filter output is missing or malformed
            echo "python=true" >> "$GITHUB_OUTPUT"
          fi

steps:
- uses: step-security/harden-runner@8d3c67de8e2fe68ef647c8db1e6a09f647780f40 # v2.19.0
with:
egress-policy: audit
- uses: actions/checkout@v4

Check failure

Code scanning / zizmor

unpinned action reference Error

unpinned action reference

Check warning

Code scanning / zizmor

credential persistence through GitHub Actions artifacts Warning

credential persistence through GitHub Actions artifacts
- uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
id: filter
with:
filters: |
python:
- 'src/**'
- 'tests/**'
- 'pyproject.toml'
- 'uv.lock'
- '.github/workflows/ci.yml'

pytest:
needs: detect
if: needs.detect.outputs.python == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
Expand Down
Loading