Skip to content

ci: build wheel

ci: build wheel #104

Workflow file for this run

name: Test
on:
push:
branches: [ main, master, develop ]
pull_request:
branches: [ main, master, develop ]
workflow_dispatch:
workflow_call:
jobs:
test-linux:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
container: python:3.12-bookworm
platform: linux_x86_64
arch: x86_64
arch_label: x86_64
- os: ubuntu-24.04-arm
container: python:3.12-bookworm
platform: linux_aarch64
arch: arm64
arch_label: arm64
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
env:
UV_LINK_MODE: copy
UV_CACHE_DIR: /github/home/.cache/uv
permissions:
actions: write
contents: read
checks: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive
- name: Cache uv downloads
uses: actions/cache@v5
with:
path: /github/home/.cache/uv
key: uvcache-${{ runner.os }}-${{ matrix.arch_label }}-${{ hashFiles('uv.lock', 'pyproject.toml') }}
restore-keys: |
uvcache-${{ runner.os }}-${{ matrix.arch_label }}-
- name: Cache uv virtual environment and build artifacts
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ matrix.arch_label }}-${{ hashFiles('uv.lock', 'pyproject.toml', 'setup.py') }}-${{ hashFiles('src/**/*.cpp', 'src/**/*.hpp', 'packaging/**/*.py') }}
restore-keys: |
venv-${{ runner.os }}-${{ matrix.arch_label }}-${{ hashFiles('uv.lock', 'pyproject.toml', 'setup.py') }}-
venv-${{ runner.os }}-${{ matrix.arch_label }}-
- name: Add Debian sid repository
run: |
echo "deb http://deb.debian.org/debian sid main" >> /etc/apt/sources.list.d/sid.list
echo 'Package: *\nPin: release a=sid\nPin-Priority: 100' > /etc/apt/preferences.d/sid
- name: Install system dependencies
run: |
apt-get update
# Ensure /etc/os-release exists (required by some actions' OS detection).
apt-get install -y base-files
apt-get install -y build-essential pkg-config libbz2-dev
apt-get install -y -t sid libmapnik-dev
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
# Work around container-job failures determining Linux distribution during
# setup-uv's built-in caching path; we cache UV_CACHE_DIR ourselves above.
enable-cache: false
- name: Install Python dependencies and build package
run: |
# Use lockfile when present; avoid doing two full syncs (which can rebuild twice).
if [ -f uv.lock ]; then
uv sync --extra test --frozen --verbose
else
uv sync --extra test --verbose
fi
- name: Run tests with coverage
run: |
uv run pytest test/python_tests -v \
--cov=mapnik \
--cov-report=term \
--cov-report=xml:coverage.xml \
--cov-report=html:htmlcov \
--junitxml=junit.xml
- name: Upload coverage reports
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-reports-linux-${{ matrix.arch_label }}
path: |
coverage.xml
htmlcov/
junit.xml
retention-days: 30
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action@v2
if: always()
with:
files: junit.xml
check_name: Test Results (Linux ${{ matrix.arch_label }})
comment_mode: off
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: always()
with:
files: coverage.xml
flags: unittests-linux-${{ matrix.arch_label }}
name: python-mapnik-coverage-linux-${{ matrix.arch_label }}
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
- name: Build sdist
run: |
uv build --sdist
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: sdist-${{ matrix.os }}-${{ matrix.arch_label }}
path: dist/*.tar.gz
retention-days: 7
test-macos:
strategy:
fail-fast: false
matrix:
include:
- os: macos-15-intel
platform: macosx_15_0_x86_64
arch: x86_64
arch_label: x86_64
- os: macos-15
platform: macosx_15_0_arm64
arch: arm64
arch_label: arm64
- os: macos-latest
platform: macosx_15_0_arm64
arch: arm64
arch_label: arm64
runs-on: ${{ matrix.os }}
env:
UV_LINK_MODE: copy
UV_CACHE_DIR: /Users/runner/.cache/uv
permissions:
actions: write
contents: read
checks: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive
- name: Cache uv downloads
uses: actions/cache@v5
with:
path: /Users/runner/.cache/uv
key: uvcache-${{ runner.os }}-${{ matrix.arch_label }}-${{ hashFiles('uv.lock', 'pyproject.toml') }}
restore-keys: |
uvcache-${{ runner.os }}-${{ matrix.arch_label }}-
- name: Cache uv virtual environment and build artifacts
uses: actions/cache@v5
with:
path: .venv
key: venv-${{ runner.os }}-${{ matrix.arch_label }}-${{ hashFiles('uv.lock', 'pyproject.toml', 'setup.py') }}-${{ hashFiles('src/**/*.cpp', 'src/**/*.hpp', 'packaging/**/*.py') }}
restore-keys: |
venv-${{ runner.os }}-${{ matrix.arch_label }}-${{ hashFiles('uv.lock', 'pyproject.toml', 'setup.py') }}-
venv-${{ runner.os }}-${{ matrix.arch_label }}-
- name: Install and cache Homebrew tools
uses: tecolicom/actions-use-homebrew-tools@v1
with:
tools: mapnik icu4c pkg-config boost gdal proj harfbuzz libx11 xorgproto libxext libxrender libxau libxdmcp xtrans
- name: Setup pkg-config and build environment
run: |
# Ensure Homebrew environment is available
eval $(brew shellenv)
# Get package-specific prefixes using brew --prefix and save to GITHUB_ENV for reuse
HOMEBREW_PREFIX=$(brew --prefix)
MAPNIK_PREFIX=$(brew --prefix mapnik)
ICU4C_PREFIX=$(brew --prefix icu4c)
BOOST_PREFIX=$(brew --prefix boost)
GDAL_PREFIX=$(brew --prefix gdal)
PROJ_PREFIX=$(brew --prefix proj)
HARFBUZZ_PREFIX=$(brew --prefix harfbuzz)
XORGPROTO_PREFIX=$(brew --prefix xorgproto)
LIBX11_PREFIX=$(brew --prefix libx11)
LIBXEXT_PREFIX=$(brew --prefix libxext)
LIBXRENDER_PREFIX=$(brew --prefix libxrender)
LIBXAU_PREFIX=$(brew --prefix libxau)
LIBXDMCP_PREFIX=$(brew --prefix libxdmcp)
XTRANS_PREFIX=$(brew --prefix xtrans)
# Find the actual xorgproto pkg-config directory (may be in Cellar with version)
XORGPROTO_PKGCONFIG_DIR=$(find "$HOMEBREW_PREFIX/Cellar/xorgproto" -type d -name "pkgconfig" 2>/dev/null | head -1)
if [ -z "$XORGPROTO_PKGCONFIG_DIR" ]; then
# Fallback to opt symlink location
XORGPROTO_PKGCONFIG_DIR="$XORGPROTO_PREFIX/share/pkgconfig"
echo "Warning: xorgproto pkgconfig directory not found in Cellar, using fallback: $XORGPROTO_PKGCONFIG_DIR"
else
echo "Found xorgproto pkgconfig directory: $XORGPROTO_PKGCONFIG_DIR"
fi
# Save prefixes to GITHUB_ENV for reuse in subsequent steps
echo "HOMEBREW_PREFIX=$HOMEBREW_PREFIX" >> $GITHUB_ENV
echo "MAPNIK_PREFIX=$MAPNIK_PREFIX" >> $GITHUB_ENV
echo "ICU4C_PREFIX=$ICU4C_PREFIX" >> $GITHUB_ENV
echo "BOOST_PREFIX=$BOOST_PREFIX" >> $GITHUB_ENV
echo "GDAL_PREFIX=$GDAL_PREFIX" >> $GITHUB_ENV
echo "PROJ_PREFIX=$PROJ_PREFIX" >> $GITHUB_ENV
echo "HARFBUZZ_PREFIX=$HARFBUZZ_PREFIX" >> $GITHUB_ENV
echo "XORGPROTO_PREFIX=$XORGPROTO_PREFIX" >> $GITHUB_ENV
echo "LIBX11_PREFIX=$LIBX11_PREFIX" >> $GITHUB_ENV
echo "LIBXEXT_PREFIX=$LIBXEXT_PREFIX" >> $GITHUB_ENV
echo "LIBXRENDER_PREFIX=$LIBXRENDER_PREFIX" >> $GITHUB_ENV
echo "LIBXAU_PREFIX=$LIBXAU_PREFIX" >> $GITHUB_ENV
echo "LIBXDMCP_PREFIX=$LIBXDMCP_PREFIX" >> $GITHUB_ENV
echo "XTRANS_PREFIX=$XTRANS_PREFIX" >> $GITHUB_ENV
echo "XORGPROTO_PKGCONFIG_DIR=$XORGPROTO_PKGCONFIG_DIR" >> $GITHUB_ENV
echo "Homebrew prefix: $HOMEBREW_PREFIX"
echo "Mapnik prefix: $MAPNIK_PREFIX"
echo "xorgproto pkg-config dir: $XORGPROTO_PKGCONFIG_DIR"
# Set PKG_CONFIG_PATH to include Homebrew's pkg-config directories
# This ensures pkg-config can find Mapnik and its dependencies (ICU, Boost, GDAL, PROJ, HarfBuzz, X11 proto, etc.)
# Note: xorgproto .pc files are in Cellar/xorgproto/<version>/share/pkgconfig
echo "PKG_CONFIG_PATH=$HOMEBREW_PREFIX/lib/pkgconfig:$HOMEBREW_PREFIX/share/pkgconfig:$ICU4C_PREFIX/lib/pkgconfig:$GDAL_PREFIX/lib/pkgconfig:$PROJ_PREFIX/lib/pkgconfig:$HARFBUZZ_PREFIX/lib/pkgconfig:$LIBX11_PREFIX/lib/pkgconfig:$LIBXEXT_PREFIX/lib/pkgconfig:$LIBXRENDER_PREFIX/lib/pkgconfig:$LIBXAU_PREFIX/lib/pkgconfig:$LIBXDMCP_PREFIX/lib/pkgconfig:$XTRANS_PREFIX/share/pkgconfig:$XORGPROTO_PKGCONFIG_DIR:$XORGPROTO_PREFIX/share/pkgconfig:$XORGPROTO_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV
# Set CXXFLAGS and LDFLAGS to include Boost, GDAL, PROJ, and HarfBuzz headers and libraries
echo "CXXFLAGS=-I$BOOST_PREFIX/include -I$GDAL_PREFIX/include -I$PROJ_PREFIX/include -I$HARFBUZZ_PREFIX/include $CXXFLAGS" >> $GITHUB_ENV
echo "LDFLAGS=-L$BOOST_PREFIX/lib -L$GDAL_PREFIX/lib -L$PROJ_PREFIX/lib -L$HARFBUZZ_PREFIX/lib $LDFLAGS" >> $GITHUB_ENV
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: false
- name: Install Python dependencies and build package
run: |
# Ensure Homebrew environment is available
eval $(brew shellenv)
# Re-export environment variables (already set in previous step via GITHUB_ENV)
# This ensures they're available in the current shell session for subprocess calls
export PKG_CONFIG_PATH="$PKG_CONFIG_PATH"
export CXXFLAGS="$CXXFLAGS"
export LDFLAGS="$LDFLAGS"
# Use lockfile when present; avoid doing two full syncs (which can rebuild twice).
if [ -f uv.lock ]; then
uv sync --extra test --frozen --verbose
else
uv sync --extra test --verbose
fi
- name: Run tests with coverage
run: |
uv run pytest test/python_tests -v \
--cov=mapnik \
--cov-report=term \
--cov-report=xml:coverage.xml \
--cov-report=html:htmlcov \
--junitxml=junit.xml
- name: Verify coverage files
if: always()
run: |
pwd
ls -la coverage.xml || echo "coverage.xml not found"
if [ -f coverage.xml ]; then
echo "✅ coverage.xml exists"
head -20 coverage.xml
else
echo "❌ coverage.xml not found"
exit 1
fi
- name: Upload coverage reports
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-reports-macos-${{ matrix.os }}-${{ matrix.arch_label }}
path: |
coverage.xml
htmlcov/
junit.xml
retention-days: 30
- name: Publish test results
uses: EnricoMi/publish-unit-test-result-action/macos@v2
if: always()
with:
files: junit.xml
check_name: Test Results (macOS ${{ matrix.os }} ${{ matrix.arch_label }})
comment_mode: off
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v5
if: always()
with:
files: coverage.xml
flags: unittests-macos-${{ matrix.os }}-${{ matrix.arch_label }}
name: python-mapnik-coverage-macos-${{ matrix.os }}-${{ matrix.arch_label }}
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
- name: Build sdist
run: |
uv build --sdist
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: sdist-${{ matrix.os }}-${{ matrix.arch_label }}
path: dist/*.tar.gz
retention-days: 7