Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 42 additions & 125 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,157 +29,70 @@ jobs:
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "version_number=${VERSION#v}" >> $GITHUB_OUTPUT

linux-build:
build-wheels:
needs: [extract-version]
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
os: [ubuntu-latest, macos-latest, macos-15-intel]
runs-on: ${{ matrix.os }}
container: ${{ matrix.container }}
env:
UV_LINK_MODE: copy
UV_CACHE_DIR: /github/home/.cache/uv
permissions:
actions: write
contents: 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: Add Debian sid repository
if: matrix.container != null
- name: Install cibuildwheel
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
python -m pip install --upgrade pip
pip install cibuildwheel

- name: Install system dependencies (Linux)
if: matrix.container != null
- name: Build wheels
env:
CIBW_BUILD_VERBOSITY: 1
CIBW_BEFORE_BUILD: "pip install uv && uv sync --frozen --no-dev || uv sync --no-dev"
run: |
apt-get update
apt-get install -y base-files
apt-get install -y build-essential pkg-config libbz2-dev
apt-get install -y -t sid libmapnik-dev
cibuildwheel --output-dir wheelhouse

- name: Install uv
uses: astral-sh/setup-uv@v7
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
enable-cache: false
name: wheels-${{ matrix.os }}
path: wheelhouse/*.whl
retention-days: 7

- name: Update version from tag
run: |
# Extract base version from pyproject.toml
BASE_VERSION=$(grep -E '^version = ' pyproject.toml | sed -E 's/^version = "([^"]+)"/\1/')
TAG=${{ needs.extract-version.outputs.version }}
VERSION="${BASE_VERSION}-${TAG}"
echo "Base version from pyproject.toml: $BASE_VERSION"
echo "Git tag: $TAG"
echo "Setting version to: $VERSION"
# Update pyproject.toml
sed -i "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml
# Update setup.py
sed -i "s/version=\"[^\"]*\"/version=\"$VERSION\"/" setup.py
# Verify changes
echo "pyproject.toml version:"
grep "^version = " pyproject.toml
echo "setup.py version:"
grep "version=" setup.py | head -1
build-sdist:
needs: [extract-version]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
submodules: recursive

- name: Build wheel and sdist
- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Build sdist
run: |
uv build --wheel --sdist
uv build --sdist

- name: Upload build artifacts
- name: Upload sdist
uses: actions/upload-artifact@v4
with:
name: dist-${{ matrix.platform }}
path: dist/
name: sdist
path: dist/*.tar.gz
retention-days: 7

# macos-build:
# needs: [extract-version]
# strategy:
# fail-fast: false
# matrix:
# include:
# - os: macos-15-intel
# platform: macosx_10_9_x86_64
# arch: x86_64
# arch_label: x86_64
# - os: macos-15
# 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: 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: Install and cache Homebrew tools
# uses: tecolicom/actions-use-homebrew-tools@v1
# with:
# tools: mapnik icu4c pkg-config
# key: mapnik-${{ matrix.arch_label }}

# - name: Install uv
# uses: astral-sh/setup-uv@v7
# with:
# enable-cache: false

# - name: Build wheel and sdist
# run: |
# uv build --wheel --sdist

# - name: Upload build artifacts
# uses: actions/upload-artifact@v4
# with:
# name: dist-${{ matrix.platform }}
# path: dist/
# retention-days: 7

release:
needs:
- extract-version
- linux-build
# - macos-build
- build-wheels
- build-sdist
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -194,8 +107,12 @@ jobs:
- name: Combine artifacts
run: |
mkdir -p dist
# Copy all wheels
find dist-all -name "*.whl" -exec cp {} dist/ \;
find dist-all -name "*.tar.gz" -exec head -1 {} \; | head -1 | xargs -I {} find dist-all -name "{}" -exec cp {} dist/ \;
# Copy sdist (should be only one)
find dist-all -name "*.tar.gz" -exec cp {} dist/ \;
echo "Artifacts in dist/:"
ls -lh dist/

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
Expand Down Expand Up @@ -250,8 +167,8 @@ jobs:
echo "### Supported Platforms:" >> $GITHUB_STEP_SUMMARY
echo "- Linux x86_64: ✅" >> $GITHUB_STEP_SUMMARY
echo "- Linux arm64: ✅" >> $GITHUB_STEP_SUMMARY
# echo "- macOS x86_64: ✅" >> $GITHUB_STEP_SUMMARY
# echo "- macOS arm64: ✅" >> $GITHUB_STEP_SUMMARY
echo "- macOS x86_64: ✅" >> $GITHUB_STEP_SUMMARY
echo "- macOS arm64: ✅" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Distribution:" >> $GITHUB_STEP_SUMMARY
echo "- GitHub Release: ✅" >> $GITHUB_STEP_SUMMARY
73 changes: 58 additions & 15 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -176,20 +176,51 @@ jobs:
- name: Install and cache Homebrew tools
uses: tecolicom/actions-use-homebrew-tools@v1
with:
tools: mapnik icu4c pkg-config boost gdal proj harfbuzz
key: mapnik-${{ matrix.arch_label }}
tools: mapnik icu4c pkg-config boost gdal proj harfbuzz libx11 xorgproto libxext libxrender libxau libxdmcp
key: mapnik-${{ matrix.os }}-${{ matrix.arch_label }}-20260118

- 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)
# 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 "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, etc.)
echo "PKG_CONFIG_PATH=$(brew --prefix)/lib/pkgconfig:$(brew --prefix)/opt/icu4c/lib/pkgconfig:$(brew --prefix)/opt/boost/lib/pkgconfig:$(brew --prefix)/opt/gdal/lib/pkgconfig:$(brew --prefix)/opt/proj/lib/pkgconfig:$(brew --prefix)/opt/harfbuzz/lib/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV
# 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:$XORGPROTO_PKGCONFIG_DIR:$XORGPROTO_PREFIX/share/pkgconfig:$PKG_CONFIG_PATH" >> $GITHUB_ENV
# Set CXXFLAGS and LDFLAGS to include Boost, GDAL, PROJ, and HarfBuzz headers and libraries
echo "CXXFLAGS=-I$(brew --prefix)/opt/boost/include -I$(brew --prefix)/opt/gdal/include -I$(brew --prefix)/opt/proj/include -I$(brew --prefix)/opt/harfbuzz/include $CXXFLAGS" >> $GITHUB_ENV
echo "LDFLAGS=-L$(brew --prefix)/opt/boost/lib -L$(brew --prefix)/opt/gdal/lib -L$(brew --prefix)/opt/proj/lib -L$(brew --prefix)/opt/harfbuzz/lib $LDFLAGS" >> $GITHUB_ENV
echo "PKG_CONFIG_PATH set to: $PKG_CONFIG_PATH"
echo "CXXFLAGS set to: $CXXFLAGS"
echo "LDFLAGS set to: $LDFLAGS"
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
Expand All @@ -198,6 +229,13 @@ jobs:

- 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
Expand Down Expand Up @@ -231,22 +269,27 @@ jobs:
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-reports-macos-${{ matrix.arch_label }}
name: coverage-reports-macos-${{ matrix.os }}-${{ matrix.arch_label }}
path: |
coverage.xml
htmlcov/
junit.xml
retention-days: 30

# Note: publish-unit-test-result-action doesn't support macOS (container action limitation)
# Test results are still uploaded as artifacts above

- 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.arch_label }}
name: python-mapnik-coverage-macos-${{ matrix.arch_label }}
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 }}
44 changes: 43 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,46 @@ testpaths = [

[tool.uv]
# Pin uv version for CI/dev consistency (used by astral-sh/setup-uv).
required-version = ">=0.9.0"
required-version = ">=0.9.0"

[tool.cibuildwheel]
# Build only Python 3.12+ (matches requires-python)
build = "cp312-*"
skip = ["*-win32", "*-manylinux_i686", "*-musllinux_*"]
test-command = "uv run pytest test/python_tests -v"
test-extras = ["test"]

# Linux build configuration
[tool.cibuildwheel.linux]
# Use manylinux_2_28 (Debian 12 / Bookworm based)
manylinux-x86_64-image = "manylinux_2_28_x86_64"
manylinux-aarch64-image = "manylinux_2_28_aarch64"

# Install system dependencies before build
before-all = """
# Add Debian sid repository for Mapnik 4.2
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

# Install build dependencies
apt-get update
apt-get install -y build-essential pkg-config libbz2-dev
apt-get install -y -t sid libmapnik-dev fonts-noto-cjk
"""

# macOS build configuration
[tool.cibuildwheel.macos]
# Build for both x86_64 and arm64
archs = ["x86_64", "arm64"]

# Install dependencies via Homebrew (run once before all builds)
before-all = "brew install mapnik icu4c pkg-config boost gdal proj harfbuzz"

# Set environment variables for each build
# These are exported in the build environment
before-build = """
eval $(brew shellenv)
export PKG_CONFIG_PATH=$(brew --prefix)/lib/pkgconfig:$(brew --prefix)/opt/icu4c/lib/pkgconfig:$(brew --prefix)/opt/boost/lib/pkgconfig:$(brew --prefix)/opt/gdal/lib/pkgconfig:$(brew --prefix)/opt/proj/lib/pkgconfig:$(brew --prefix)/opt/harfbuzz/lib/pkgconfig:$PKG_CONFIG_PATH
export CXXFLAGS="-I$(brew --prefix)/opt/boost/include -I$(brew --prefix)/opt/gdal/include -I$(brew --prefix)/opt/proj/include -I$(brew --prefix)/opt/harfbuzz/include $CXXFLAGS"
export LDFLAGS="-L$(brew --prefix)/opt/boost/lib -L$(brew --prefix)/opt/gdal/lib -L$(brew --prefix)/opt/proj/lib -L$(brew --prefix)/opt/harfbuzz/lib $LDFLAGS"
"""
Loading