diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52f1b66..bb34e7e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,13 +6,24 @@ on: tags: - "v*" # Semantic version tags pull_request: +permissions: + contents: read jobs: test: strategy: matrix: # py: ["3.10", "3.11", "3.12", "3.13", "3.14"] py: ["3.11", "3.12", "3.13", "3.14"] - runs-on: ubuntu-latest + os: [ubuntu-latest, macos-latest] + exclude: + # Only test the latest stable Python on macOS to keep CI fast + - os: macos-latest + py: "3.11" + - os: macos-latest + py: "3.12" + - os: macos-latest + py: "3.14" + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 with: @@ -28,6 +39,30 @@ jobs: run: uv sync --group dev - name: pytest run: uv run pytest -m "not slow" . + sanitize: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.13" + - name: Install uv + run: pip install uv + - name: Build with ASAN + UBSAN + env: + CMAKE_ARGS: "-DDML_ENABLE_ASAN=ON -DDML_ENABLE_UBSAN=ON" + run: uv sync --group dev + - name: Run tests with sanitizers + env: + ASAN_OPTIONS: "detect_leaks=0:abort_on_error=1" + UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1" + run: | + LIBASAN=$(gcc -print-file-name=libasan.so) + LD_PRELOAD="$LIBASAN" uv run pytest -m "not slow" . lint: runs-on: ubuntu-latest steps: diff --git a/CMakeLists.txt b/CMakeLists.txt index df96a50..2cfcbeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -99,10 +99,10 @@ target_link_libraries(_db PRIVATE daggerml_core lmdb msgpackc sha256 Python::Mod if(_dml_sanitize_flags) target_compile_options(_db PRIVATE ${_dml_sanitize_flags}) target_link_options(_db PRIVATE ${_dml_sanitize_flags}) - if(DML_ENABLE_ASAN) + if(DML_ENABLE_ASAN AND NOT APPLE AND CMAKE_C_COMPILER_ID MATCHES "Clang") target_link_options(_db PRIVATE "-shared-libasan") endif() - if(DML_ENABLE_UBSAN) + if(DML_ENABLE_UBSAN AND CMAKE_C_COMPILER_ID MATCHES "Clang") target_compile_options(_db PRIVATE "-fno-sanitize=function") endif() endif() diff --git a/tests/contrib/test_docker_executor.py b/tests/contrib/test_docker_executor.py index 4d8c8d9..60f7c7b 100644 --- a/tests/contrib/test_docker_executor.py +++ b/tests/contrib/test_docker_executor.py @@ -2,6 +2,7 @@ import json import os +import shutil from pathlib import Path from typing import Any, cast @@ -20,6 +21,9 @@ def _reset_registries(tmp_path, monkeypatch): areg._reset_for_tests() ereg._reset_for_tests() monkeypatch.setenv("DML_FN_CACHE_DIR", str(tmp_path / "state")) + # Ensure docker_bin resolves on platforms without docker (tests mock all docker calls) + _orig_which = shutil.which + monkeypatch.setattr(shutil, "which", lambda n: "/usr/bin/docker" if n == "docker" else _orig_which(n)) areg.register_adapter(LocalAdapter) ereg.register_executor(ScriptExecutor) ereg.register_executor(DockerExecutor)