Unit Testing and Deployment #132
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Unit Testing and Deployment | |
| # zizmor ignore note: All caching for pushes to main should be disabled with the `USE_CACHE` env var | |
| on: # zizmor: ignore[cache-poisoning] | |
| pull_request: | |
| merge_group: | |
| workflow_dispatch: | |
| schedule: | |
| - cron: "0 4 * * *" | |
| push: | |
| tags: | |
| - "*" | |
| branches: | |
| - main | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| ALLOW_PLOTTING: true | |
| SHELLOPTS: "errexit:pipefail" | |
| TOX_COLORED: "yes" | |
| USE_CACHE: ${{ | |
| ( | |
| (github.event_name == 'workflow_dispatch' && github.event.inputs.cache == 'true') || | |
| (github.event_name == 'pull_request') || | |
| (github.event_name == 'push') | |
| ) && | |
| !startsWith(github.ref, 'refs/tags/v') && | |
| !startsWith(github.ref, 'refs/heads/release/') && | |
| !startsWith(github.ref, 'refs/heads/main') | |
| }} | |
| CACHE_FOLDER_NAME: pyvista-data | |
| PYVISTA_VTK_DATA: ${{ github.workspace }}/pyvista-data | |
| permissions: | |
| id-token: none | |
| jobs: | |
| # Save PR metadata as an artifact so the separate deploy workflow (triggered | |
| # via workflow_run) can associate the build with its PR — workflow_run events | |
| # do not include pull_request data for fork PRs. | |
| pr-info: | |
| name: Save PR info | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Save PR metadata | |
| env: | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| IS_FORK: ${{ github.event.pull_request.head.repo.fork }} | |
| run: | | |
| mkdir -p pr-info | |
| printf '%s' "$PR_NUMBER" > pr-info/pr_number.txt | |
| printf '%s' "$HEAD_SHA" > pr-info/head_sha.txt | |
| printf '%s' "$IS_FORK" > pr-info/is_fork.txt | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: pr-info | |
| path: pr-info/ | |
| # Cache pyvista/data for each runner separately | |
| # Runner labels should match the actual runners used by the different jobs in this file | |
| # GitHub-hosted runners can re-use the cache across OS's. | |
| # Here we run the cache on ubuntu, but it's usable by windows GitHub runners too | |
| cache-github-hosted: | |
| name: Cache pyvista/data | |
| uses: ./.github/workflows/cache-pyvista-data.yml | |
| with: | |
| runs-on: "ubuntu-22.04" | |
| # The macOS self-hosted runner needs its own cache (GitHub-hosted runners | |
| # share cache-github-hosted across Linux and Windows). | |
| cache-macOS-self-hosted: | |
| name: Cache pyvista/data | |
| uses: ./.github/workflows/cache-pyvista-data.yml | |
| with: | |
| runs-on: "macos-15-self-hosted" | |
| macOS: | |
| name: ${{ matrix.name }} | |
| needs: cache-macOS-self-hosted | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # Self-hosted runner configurations | |
| - name: MacOS Unit Testing (3.10) | |
| python-version: "3.10" | |
| runner-labels: "macos-15-self-hosted" | |
| - name: MacOS Unit Testing (3.11) | |
| python-version: "3.11" | |
| runner-labels: "macos-15-self-hosted" | |
| - name: MacOS Unit Testing (3.12) | |
| python-version: "3.12" | |
| runner-labels: "macos-15-self-hosted" | |
| - name: MacOS Unit Testing (3.13) | |
| python-version: "3.13" | |
| runner-labels: "macos-15-self-hosted" | |
| - name: MacOS Unit Testing (3.14) | |
| python-version: "3.14" | |
| runner-labels: "macos-15-self-hosted" | |
| runs-on: ${{ matrix.runner-labels }} | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python ${{ matrix.python-version }} | |
| if: runner.environment != 'self-hosted' | |
| uses: actions/setup-python@v7 | |
| continue-on-error: true | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: ${{ env.USE_CACHE == 'true' && 'pip' || '' }} | |
| - name: Set Python PATH and create a virtual env | |
| if: runner.environment == 'self-hosted' | |
| run: | | |
| /opt/homebrew/bin/python${{ matrix.python-version }} -m venv .venv | |
| echo "${{ github.workspace }}/.venv/bin" >> $GITHUB_PATH | |
| - name: Restore pyvista/data cache | |
| uses: actions/cache/restore@v6 | |
| with: | |
| path: ${{ env.CACHE_FOLDER_NAME }} | |
| key: ${{ needs.cache-macOS-self-hosted.outputs.key }} | |
| enableCrossOsArchive: true | |
| - name: Install tox-uv | |
| run: pip install tox-uv | |
| - name: Core Testing (no GL) | |
| run: tox run -e py${{ matrix.python-version }}-core | |
| - name: Plotting Testing (uses GL) | |
| if: ${{ !cancelled() }} | |
| run: tox run -e py${{ matrix.python-version }}-plotting | |
| - name: Upload Images for Failed Tests | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: failed_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _failed_test_images | |
| - name: Upload Generated Images | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: generated_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _generated_test_images | |
| Linux: | |
| name: Linux Unit Testing (${{ matrix.python-version }}, ${{ matrix.vtk-version }}, ${{ matrix.numpy-version }}) | |
| runs-on: ubuntu-22.04 | |
| needs: cache-github-hosted | |
| strategy: | |
| fail-fast: false | |
| # see discussion at https://github.com/pyvista/pyvista/issues/2867 | |
| matrix: | |
| include: | |
| # numeric numpy versions are ~= conditions, e.g. "1.23" means "numpy~=1.23.0" | |
| - python-version: "3.10" | |
| vtk-version: "9.3.1" | |
| numpy-version: "latest" | |
| - python-version: "3.11" | |
| vtk-version: "9.4.2" | |
| numpy-version: "latest" | |
| - python-version: "3.12" | |
| vtk-version: "9.5.2" | |
| numpy-version: "latest" | |
| - python-version: "3.13" | |
| vtk-version: "9.6.2" | |
| numpy-version: "latest" | |
| - python-version: "3.14" | |
| vtk-version: "latest" | |
| numpy-version: "latest" | |
| - python-version: "3.14" | |
| vtk-version: "latest" | |
| numpy-version: "nightly" | |
| env: | |
| TOX_FACTOR: py${{matrix.python-version}}-numpy_${{matrix.numpy-version}}-vtk_${{matrix.vtk-version}} | |
| PARALLEL: "-n auto" # Use all available cores on the selected runner | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 2 | |
| persist-credentials: false | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 | |
| with: | |
| packages: xvfb libgl1 libglx-mesa0 libosmesa6 | |
| version: 3.0 | |
| - name: Restore pyvista/data cache | |
| uses: actions/cache/restore@v6 | |
| with: | |
| path: ${{ env.CACHE_FOLDER_NAME }} | |
| key: ${{ needs.cache-github-hosted.outputs.key }} | |
| enableCrossOsArchive: true | |
| - name: Install tox-uv | |
| run: pip install tox-uv | |
| - name: Core Testing (no GL) | |
| env: | |
| TOX_ENV: ${{ env.TOX_FACTOR }}-cov-core | |
| run: tox run -e "$TOX_ENV" | |
| - name: Plotting Testing (uses GL) | |
| if: ${{ !cancelled() }} | |
| env: | |
| TOX_ENV: ${{ env.TOX_FACTOR }}-cov-plotting | |
| run: xvfb-run tox run -e "$TOX_ENV" | |
| - name: Upload Images for Failed Tests | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: failed_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _failed_test_images | |
| - name: Upload Generated Images | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: generated_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _generated_test_images | |
| - name: Upload coverage reports | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage_reports-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: coverage*.xml | |
| if-no-files-found: error | |
| - name: Check package | |
| run: | | |
| pip install build twine | |
| python -m build | |
| twine check --strict dist/* | |
| VTK-dev: | |
| # For PRs, only run this job if the 'vtk-dev-testing' label is applied | |
| if: | | |
| (github.event_name != 'pull_request' && github.event_name != 'push' && github.event_name != 'merge_group') || | |
| contains(github.event.pull_request.labels.*.name, 'vtk-dev-testing') | |
| name: Linux VTK Dev Testing | |
| permissions: | |
| contents: read | |
| runs-on: ubuntu-22.04 | |
| needs: cache-github-hosted | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.14"] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 2 | |
| persist-credentials: false | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: "pip" | |
| - uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 | |
| with: | |
| packages: xvfb libgl1 libglx-mesa0 libosmesa6 | |
| version: 3.0 | |
| - name: Restore pyvista/data cache | |
| uses: actions/cache/restore@v6 | |
| with: | |
| path: ${{ env.CACHE_FOLDER_NAME }} | |
| key: ${{ needs.cache-github-hosted.outputs.key }} | |
| enableCrossOsArchive: true | |
| - name: Install tox-uv | |
| run: pip install tox-uv | |
| - name: Core Testing (no GL) | |
| run: tox run -e py${{ matrix.python-version }}-cov-core-vtk_dev | |
| - name: Plotting Testing (uses GL) | |
| if: ${{ !cancelled() }} | |
| run: xvfb-run tox run -e py${{ matrix.python-version }}-cov-plotting-vtk_dev | |
| - name: Upload Images for Failed Tests | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: failed_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _failed_test_images | |
| - name: Upload Generated Images | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: generated_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _generated_test_images | |
| - name: Upload coverage reports | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: coverage_reports-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: coverage*.xml | |
| if-no-files-found: error | |
| windows: | |
| name: Windows Unit Testing | |
| runs-on: windows-latest | |
| timeout-minutes: 80 | |
| needs: cache-github-hosted | |
| env: | |
| CI_WINDOWS: true | |
| PARALLEL: "-n auto" # Use all available cores on the selected runner | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| cache: "pip" | |
| - name: Restore pyvista/data cache | |
| uses: actions/cache/restore@v6 | |
| with: | |
| path: ${{ env.CACHE_FOLDER_NAME }} | |
| key: ${{ needs.cache-github-hosted.outputs.key }} | |
| enableCrossOsArchive: true | |
| - name: Set up headless display | |
| uses: pyvista/setup-headless-display-action@5bc8de3bc71fcda7a96439571287a554901541a0 | |
| - name: Install tox-uv | |
| run: pip install tox-uv | |
| - name: Core Testing (no GL) | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 | |
| with: | |
| timeout_minutes: 12 | |
| max_attempts: 3 | |
| retry_on: timeout | |
| command: tox run -e py${{ matrix.python-version }}-core | |
| - name: Plotting Testing (uses GL) | |
| uses: nick-fields/retry@ad984534de44a9489a53aefd81eb77f87c70dc60 | |
| if: ${{ !cancelled() }} | |
| with: | |
| timeout_minutes: 30 | |
| max_attempts: 3 | |
| retry_on: timeout | |
| command: tox run -e py${{ matrix.python-version }}-plotting | |
| - name: Upload Images for Failed Tests | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: failed_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _failed_test_images | |
| - name: Upload Generated Images | |
| if: always() | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: generated_test_images-${{ github.job }}-${{ join(matrix.* , '-') }} | |
| path: _generated_test_images | |
| # The test image report Netlify deploy lives in test-image-report-deploy.yml, | |
| # triggered via workflow_run, so fork PR CI is never blocked on deployment | |
| # approval. | |
| upload-coverage-: | |
| name: Upload coverage reports | |
| runs-on: ubuntu-22.04 | |
| needs: [Linux, VTK-dev] | |
| # `always()` ensures this still runs when VTK-dev is skipped because the | |
| # 'vtk-dev-testing' label is not applied. When VTK-dev does run, its | |
| # coverage artifacts get bundled into this single CodeCov upload. | |
| # Run on push/merge_group as well as pull_request so Codecov's | |
| # main-branch baseline keeps advancing with every merge. | |
| if: always() && needs.Linux.result == 'success' | |
| permissions: | |
| # id-token: write authorizes OIDC token minting so codecov-action can | |
| # authenticate to Codecov without a long-lived CODECOV_TOKEN secret. | |
| id-token: write | |
| contents: read | |
| steps: | |
| # Needs to checkout only for codecov to match file names | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 2 | |
| persist-credentials: false | |
| - name: Download coverage reports | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: coverage_reports-* | |
| path: coverage*.xml | |
| - name: Upload coverage to CodeCov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f | |
| with: | |
| fail_ci_if_error: true | |
| use_oidc: true | |
| # Publish to PyPI using trusted publishing | |
| release: | |
| name: Release | |
| if: github.event_name == 'push' && contains(github.ref, 'refs/tags') | |
| needs: [Linux, windows, macOS] | |
| runs-on: ubuntu-24.04 | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/pyvista | |
| permissions: | |
| id-token: write # this permission is mandatory for trusted publishing | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - name: Set up Python | |
| uses: actions/setup-python@v7 | |
| with: | |
| python-version: 3.14 | |
| - name: Build package | |
| run: | | |
| pip install build | |
| python -m build | |
| - name: Upload to PyPi | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 |