Skip to content

Expose detect_ridges (-nr) toggle on typed MMG options #1357

Expose detect_ridges (-nr) toggle on typed MMG options

Expose detect_ridges (-nr) toggle on typed MMG options #1357

Workflow file for this run

# .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