From 564dcde2adf60b53cd2bc220b87f2699856cbdfb Mon Sep 17 00:00:00 2001 From: justincasher Date: Sat, 9 May 2026 10:57:00 -0700 Subject: [PATCH 1/2] ci: add Python CI workflow for ruff + pytest under python/ Mirrors the pattern from numina/fuse: - astral-sh/setup-uv with cache enabled - `uv sync --locked --group ` so CI fails when uv.lock drifts - Separate lint and test jobs Triggers only when python/ or this workflow file changes, with concurrency.cancel-in-progress for superseded runs and read-only GITHUB_TOKEN permissions. The pytest step is guarded against "no tests collected" since python/tests/ is empty so far. Remove the guard once real tests land. --- .github/workflows/python_ci.yml | 71 +++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/python_ci.yml diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml new file mode 100644 index 0000000000..a2d38c3a59 --- /dev/null +++ b/.github/workflows/python_ci.yml @@ -0,0 +1,71 @@ +name: Python CI + +on: + push: + branches: + - main + paths: + - 'python/**' + - '.github/workflows/python_ci.yml' + pull_request: + branches: + - main + paths: + - 'python/**' + - '.github/workflows/python_ci.yml' + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + runs-on: ubuntu-latest + name: Lint (ruff) + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Install dev dependencies + run: cd python && uv sync --locked --group dev + + - name: Ruff check + run: cd python && uv run ruff check . + + - name: Ruff format check + run: cd python && uv run ruff format --check . + + test: + runs-on: ubuntu-latest + name: Test (pytest) + steps: + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - uses: astral-sh/setup-uv@v6 + with: + enable-cache: true + + - name: Install test dependencies + run: cd python && uv sync --locked --group test + + - name: Pytest + run: | + cd python + # Skip cleanly when no tests have been written yet (pytest exits 5 + # on "no tests collected"). Remove this guard once tests exist. + if compgen -G "tests/test_*.py" > /dev/null || compgen -G "tests/**/test_*.py" > /dev/null; then + uv run pytest --cov + else + echo "No test files under tests/; skipping pytest." + fi From 3a65ebe134e8cc8091e79f95226d8aeeb25939d3 Mon Sep 17 00:00:00 2001 From: justincasher Date: Sat, 9 May 2026 11:00:47 -0700 Subject: [PATCH 2/2] ci: harden pytest guard by translating exit code 5 instead of glob check The previous glob (tests/test_*.py or tests/**/test_*.py) was fragile: it missed *_test.py files and bash globstar isn't enabled by default, so the recursive pattern didn't match nested tests. Run pytest unconditionally and treat its "no tests collected" exit code (5) as success; any other failure still fails the job. --- .github/workflows/python_ci.yml | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/python_ci.yml b/.github/workflows/python_ci.yml index a2d38c3a59..d47a2c9999 100644 --- a/.github/workflows/python_ci.yml +++ b/.github/workflows/python_ci.yml @@ -62,10 +62,15 @@ jobs: - name: Pytest run: | cd python - # Skip cleanly when no tests have been written yet (pytest exits 5 - # on "no tests collected"). Remove this guard once tests exist. - if compgen -G "tests/test_*.py" > /dev/null || compgen -G "tests/**/test_*.py" > /dev/null; then - uv run pytest --cov - else - echo "No test files under tests/; skipping pytest." + # Translate pytest's exit code 5 ("no tests collected") to success + # so CI passes while tests/ is still empty. Remove this once tests + # exist; any other failure still fails the job. + set +e + uv run pytest --cov + status=$? + set -e + if [ "$status" -eq 5 ]; then + echo "No tests collected; skipping pytest." + exit 0 fi + exit "$status"