Expose detect_ridges (-nr) toggle on typed MMG options #1357
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
| # .github/workflows/build.yml | |
| name: Build Wheels | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| release: | |
| types: [published] | |
| workflow_dispatch: | |
| inputs: | |
| publish_to_pypi: | |
| description: "Publish to PyPI" | |
| required: false | |
| default: false | |
| type: boolean | |
| publish_to_testpypi: | |
| description: "Publish to TestPyPI and run verification tests" | |
| required: false | |
| default: false | |
| type: boolean | |
| jobs: | |
| build-wheels-linux-x86_64: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| - name: Build manylinux_2_28 x86_64 wheels | |
| env: | |
| CIBW_BUILD: ${{ github.event_name == 'pull_request' && 'cp310-* cp314-*' || '' }} | |
| CIBW_ARCHS_LINUX: x86_64 | |
| CIBW_MANYLINUX_X86_64_IMAGE: manylinux_2_28 | |
| uses: pypa/cibuildwheel@v3.4.1 | |
| - name: Upload wheels as artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-linux-x86_64 | |
| path: wheelhouse/*.whl | |
| retention-days: 30 | |
| - name: Report wheel sizes | |
| shell: bash | |
| run: | | |
| echo "## Wheel Sizes for Linux x86_64" >> $GITHUB_STEP_SUMMARY | |
| echo "| Wheel | Size |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|------|" >> $GITHUB_STEP_SUMMARY | |
| for wheel in wheelhouse/*.whl; do | |
| if [ -f "$wheel" ]; then | |
| size=$(du -h "$wheel" | cut -f1) | |
| name=$(basename "$wheel") | |
| echo "| $name | $size |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Total wheelhouse size:** $(du -sh wheelhouse/ | cut -f1)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| build-wheels-linux-aarch64: | |
| runs-on: ubuntu-24.04-arm # Native ARM runner (no QEMU needed) | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| - name: Build manylinux_2_28 aarch64 wheels | |
| env: | |
| CIBW_BUILD: ${{ github.event_name == 'pull_request' && 'cp310-* cp314-*' || '' }} | |
| CIBW_ARCHS_LINUX: aarch64 | |
| CIBW_MANYLINUX_AARCH64_IMAGE: manylinux_2_28 | |
| uses: pypa/cibuildwheel@v3.4.1 | |
| - name: Upload wheels as artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-linux-aarch64 | |
| path: wheelhouse/*.whl | |
| retention-days: 30 | |
| - name: Report wheel sizes | |
| shell: bash | |
| run: | | |
| echo "## Wheel Sizes for Linux aarch64" >> $GITHUB_STEP_SUMMARY | |
| echo "| Wheel | Size |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|------|" >> $GITHUB_STEP_SUMMARY | |
| for wheel in wheelhouse/*.whl; do | |
| if [ -f "$wheel" ]; then | |
| size=$(du -h "$wheel" | cut -f1) | |
| name=$(basename "$wheel") | |
| echo "| $name | $size |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Total wheelhouse size:** $(du -sh wheelhouse/ | cut -f1)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| build-wheels-windows: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| # ``repair-wheel-command`` is overridden in pyproject.toml so that | |
| # delvewheel vendors the MSVC runtime into ``mmgpy.libs/`` (see #299). | |
| # Overriding the default also disables cibuildwheel's auto-install of | |
| # delvewheel, so we put it on PATH ourselves. ``uv tool install`` | |
| # links the entry-point into uv's tool bin dir, which setup-uv has | |
| # already added to PATH for the rest of the job (cibuildwheel | |
| # subprocesses inherit it). | |
| - name: Install delvewheel for the override repair-wheel-command | |
| shell: bash | |
| run: uv tool install delvewheel | |
| - name: Build wheels | |
| env: | |
| CIBW_BUILD: ${{ github.event_name == 'pull_request' && 'cp310-* cp314-*' || '' }} | |
| uses: pypa/cibuildwheel@v3.4.1 | |
| - name: Upload wheels as artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-windows | |
| path: wheelhouse/*.whl | |
| retention-days: 30 | |
| - name: Report wheel sizes | |
| shell: bash | |
| run: | | |
| echo "## Wheel Sizes for Windows" >> $GITHUB_STEP_SUMMARY | |
| echo "| Wheel | Size |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|------|" >> $GITHUB_STEP_SUMMARY | |
| for wheel in wheelhouse/*.whl; do | |
| if [ -f "$wheel" ]; then | |
| size=$(du -h "$wheel" | cut -f1) | |
| name=$(basename "$wheel") | |
| echo "| $name | $size |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Total wheelhouse size:** $(du -sh wheelhouse/ | cut -f1)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| build-wheels-macos: | |
| runs-on: macos-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| - name: Build wheels | |
| env: | |
| CIBW_BUILD: ${{ github.event_name == 'pull_request' && 'cp310-* cp314-*' || '' }} | |
| uses: pypa/cibuildwheel@v3.4.1 | |
| - name: Upload wheels as artifacts | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: wheels-macos | |
| path: wheelhouse/*.whl | |
| retention-days: 30 | |
| - name: Report wheel sizes | |
| shell: bash | |
| run: | | |
| echo "## Wheel Sizes for macOS" >> $GITHUB_STEP_SUMMARY | |
| echo "| Wheel | Size |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------|------|" >> $GITHUB_STEP_SUMMARY | |
| for wheel in wheelhouse/*.whl; do | |
| if [ -f "$wheel" ]; then | |
| size=$(du -h "$wheel" | cut -f1) | |
| name=$(basename "$wheel") | |
| echo "| $name | $size |" >> $GITHUB_STEP_SUMMARY | |
| fi | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Total wheelhouse size:** $(du -sh wheelhouse/ | cut -f1)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| build-sdist: | |
| runs-on: ubuntu-latest | |
| name: Build source distribution | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| - name: Install build dependencies | |
| run: | | |
| uv venv -p 3.13 | |
| uv pip install scikit-build-core pybind11 numpy | |
| - name: Build source distribution | |
| run: uv build --sdist | |
| - name: Upload sdist as artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: sdist | |
| path: dist/*.tar.gz | |
| retention-days: 30 | |
| validate-release: | |
| # Release gate: install the freshly built wheel and run docs codeblocks + | |
| # examples against it before any PyPI / TestPyPI upload. This is what | |
| # catches a release shipping with broken examples or doc snippets, which | |
| # is exactly how v0.12.0 went out with two crashing implicit-domain | |
| # examples. | |
| needs: [build-wheels-linux-x86_64, build-sdist] | |
| runs-on: ubuntu-latest | |
| name: Validate release (docs codeblocks + examples) | |
| # Also run on pull_request so we catch regressions in docs codeblocks / | |
| # examples before tagging, not after; v0.16.0 shipped a slim install | |
| # that broke pytest-pyvista's PIL import and the publish chain only | |
| # surfaced it during the wheel-build dispatch. | |
| if: github.event_name == 'pull_request' || (github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_dispatch' && (inputs.publish_to_pypi || inputs.publish_to_testpypi)) | |
| timeout-minutes: 30 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| # examples_test.py discovers examples via `git ls-files`, so the | |
| # checkout must include the .git directory. | |
| fetch-depth: 0 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| enable-cache: true | |
| - name: Install GL and start Xvfb | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y libgl1-mesa-dev libosmesa6-dev xvfb | |
| Xvfb :99 -screen 0 1024x768x24 & | |
| echo "DISPLAY=:99" >> $GITHUB_ENV | |
| - name: Download linux x86_64 wheels | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: wheels-linux-x86_64 | |
| path: wheelhouse/ | |
| - name: Create venv and install the freshly built wheel | |
| run: | | |
| uv venv --python 3.10 .venv | |
| shopt -s nullglob | |
| wheels=(wheelhouse/mmgpy-*-cp310-cp310-manylinux*.whl) | |
| test "${#wheels[@]}" -eq 1 | |
| # Install the artifact by path so PyPI can never satisfy the mmgpy | |
| # requirement. PyPI remains available for runtime dependencies. | |
| uv pip install --python .venv/bin/python "${wheels[0]}" 'pyvista>=0.48,<1' | |
| # fedoo is needed for the elasticity_propagation example. | |
| uv pip install --python .venv/bin/python pytest pytest-examples pytest-pyvista fedoo | |
| - name: Verify the installed mmgpy is the wheel | |
| run: | | |
| .venv/bin/python -c "import mmgpy; from mmgpy._mmgpy import MmgMesh3D; print(mmgpy.__version__, mmgpy.__file__); assert 'site-packages' in mmgpy.__file__; assert 'surface_only' in MmgMesh3D.remesh_levelset.__doc__" | |
| - name: Test documentation code blocks | |
| env: | |
| PYVISTA_OFF_SCREEN: "true" | |
| MPLBACKEND: Agg | |
| run: .venv/bin/pytest tests/docs_test.py -v | |
| - name: Run examples (smoke; image-regression diffs are a build-and-test concern) | |
| env: | |
| PYVISTA_OFF_SCREEN: "true" | |
| MPLBACKEND: Agg | |
| run: .venv/bin/pytest tests/examples_test.py -v -k "not test_pyvista_example" | |
| publish-to-pypi: | |
| needs: | |
| [ | |
| build-wheels-linux-x86_64, | |
| build-wheels-linux-aarch64, | |
| build-wheels-windows, | |
| build-wheels-macos, | |
| build-sdist, | |
| validate-release, | |
| ] | |
| runs-on: ubuntu-latest | |
| name: Publish to PyPI | |
| if: (github.event_name == 'release' && github.event.action == 'published') || (github.event_name == 'workflow_dispatch' && inputs.publish_to_pypi) | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/p/mmgpy | |
| permissions: | |
| id-token: write # For trusted publishing | |
| actions: read | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| merge-multiple: true | |
| path: dist/ | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| skip-existing: true | |
| attestations: false | |
| verbose: true | |
| # TestPyPI publishing (manual trigger only) | |
| # Prerequisites: | |
| # 1. Create a "testpypi" environment in GitHub repo settings | |
| # 2. Configure trusted publishing on TestPyPI: | |
| # https://test.pypi.org/manage/account/publishing/ | |
| # Add: owner=kmarchais, repo=mmgpy, workflow=build-wheels.yml, environment=testpypi | |
| publish-to-testpypi: | |
| needs: | |
| [ | |
| build-wheels-linux-x86_64, | |
| build-wheels-linux-aarch64, | |
| build-wheels-windows, | |
| build-wheels-macos, | |
| build-sdist, | |
| validate-release, | |
| ] | |
| runs-on: ubuntu-latest | |
| name: Publish to TestPyPI | |
| if: github.event_name == 'workflow_dispatch' && inputs.publish_to_testpypi | |
| environment: | |
| name: testpypi | |
| url: https://test.pypi.org/p/mmgpy | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| merge-multiple: true | |
| path: dist/ | |
| - name: List artifacts | |
| run: ls -la dist/ | |
| - name: Publish to TestPyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| verbose: true | |
| attestations: false | |
| test-uvx-testpypi: | |
| needs: publish-to-testpypi | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| name: Test uvx (${{ matrix.os }}) | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.12" | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| - name: Wait for TestPyPI to index package | |
| shell: bash | |
| run: | | |
| echo "Waiting 60 seconds for TestPyPI to index the package..." | |
| sleep 60 | |
| - name: Get package version | |
| id: version | |
| shell: bash | |
| run: | | |
| VERSION=$(curl -s https://test.pypi.org/pypi/mmgpy/json | python3 -c "import sys, json; print(json.load(sys.stdin)['info']['version'])") | |
| echo "version=$VERSION" >> $GITHUB_OUTPUT | |
| echo "Latest version on TestPyPI: $VERSION" | |
| - name: Test uvx mmgpy --help | |
| shell: bash | |
| run: | | |
| uvx --python 3.12 \ | |
| --index-url https://test.pypi.org/simple/ \ | |
| --extra-index-url https://pypi.org/simple/ \ | |
| --index-strategy unsafe-best-match \ | |
| --from "mmgpy==${{ steps.version.outputs.version }}" \ | |
| mmgpy --help | |
| - name: Test uvx mmgpy --version | |
| shell: bash | |
| run: | | |
| uvx --python 3.12 \ | |
| --index-url https://test.pypi.org/simple/ \ | |
| --extra-index-url https://pypi.org/simple/ \ | |
| --index-strategy unsafe-best-match \ | |
| --from "mmgpy==${{ steps.version.outputs.version }}" \ | |
| mmgpy --version | |
| - name: Test python -m mmgpy | |
| shell: bash | |
| run: | | |
| # Run from /tmp to avoid uv detecting local pyproject.toml | |
| cd /tmp | |
| uv run --python 3.12 \ | |
| --index-url https://test.pypi.org/simple/ \ | |
| --extra-index-url https://pypi.org/simple/ \ | |
| --index-strategy unsafe-best-match \ | |
| --with "mmgpy==${{ steps.version.outputs.version }}" \ | |
| python -m mmgpy --help | |
| - name: Test remeshing with sample mesh | |
| shell: bash | |
| run: | | |
| # Use the Suzanne mesh from the repo for testing | |
| uvx --python 3.12 \ | |
| --index-url https://test.pypi.org/simple/ \ | |
| --extra-index-url https://pypi.org/simple/ \ | |
| --index-strategy unsafe-best-match \ | |
| --from "mmgpy==${{ steps.version.outputs.version }}" \ | |
| mmgpy assets/Suzanne.stl -o remeshed.stl -hausd 0.05 || true | |
| # Check if output was created | |
| if [ -f remeshed.stl ]; then | |
| echo "Remeshing successful!" | |
| ls -la remeshed.stl | |
| else | |
| echo "Note: Remeshing may have failed or mesh file not available" | |
| fi | |
| - name: Summary | |
| shell: bash | |
| run: | | |
| echo "## TestPyPI Verification Results" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "- **OS:** ${{ matrix.os }}" >> $GITHUB_STEP_SUMMARY | |
| echo "- **uvx mmgpy --help:** Passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- **uvx mmgpy --version:** Passed" >> $GITHUB_STEP_SUMMARY | |
| echo "- **python -m mmgpy:** Passed" >> $GITHUB_STEP_SUMMARY | |
| deploy-versioned-docs: | |
| # Versioned docs deploy is gated on a successful PyPI upload so we never | |
| # publish docs for a release whose wheel never made it to PyPI. Pre-release | |
| # tags skip this step (matches the previous behaviour in docs.yml). | |
| needs: publish-to-pypi | |
| runs-on: ubuntu-latest | |
| name: Deploy versioned docs | |
| if: >- | |
| (github.event_name == 'release' && | |
| github.event.action == 'published' && | |
| !github.event.release.prerelease) || | |
| (github.event_name == 'workflow_dispatch' && | |
| inputs.publish_to_pypi && | |
| startsWith(github.ref, 'refs/tags/v')) | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: docs-deploy | |
| cancel-in-progress: false | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| enable-cache: true | |
| - run: uv python install 3.12 | |
| - uses: awalsh128/cache-apt-pkgs-action@553a35bb8ebd9fcabcb1c9451aa4c98e1b4ca8a9 # v1.6.3 | |
| with: | |
| packages: libvtk9-dev qtbase5-dev qt5-qmake libgl1-mesa-dev libglu1-mesa-dev freeglut3-dev | |
| version: 1.0 | |
| - run: uv sync --group docs | |
| - name: Configure Git for mike | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Deploy versioned docs with mike | |
| run: | | |
| VERSION="${GITHUB_REF#refs/tags/v}" | |
| MINOR_VERSION=$(echo "$VERSION" | cut -d. -f1-2) | |
| uv run mike deploy "$MINOR_VERSION" latest --update-aliases --push | |
| # Idempotent: ensures root URL always redirects to latest. | |
| uv run mike set-default latest --push |