Repository navigation
fix(tools): raise TypeError for a non-boolean exclusive flag in the c… #181
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
| name: Develop CI | |
| # Fast feedback for day-to-day work on develop: formatting, linting, and a | |
| # single-platform (Linux/Debug) build + test. The heavy, thorough validation | |
| # (multi-OS native, ASan/UBSan, coverage, WASM) runs as the pre-release gate in | |
| # ci.yml on pull requests to main and pushes to main. | |
| on: | |
| push: | |
| branches: [develop] | |
| tags-ignore: ['**'] | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'LICENSE' | |
| - 'NOTICE' | |
| pull_request: | |
| branches: [develop] | |
| paths-ignore: | |
| - '**.md' | |
| - 'docs/**' | |
| - 'LICENSE' | |
| - 'NOTICE' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| clang-format: | |
| name: clang-format | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install clang-format | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y clang-format | |
| - name: Check formatting | |
| run: | | |
| git ls-files -z -- '*.h' '*.hpp' '*.c' '*.cpp' '*.mm' ':!:third_party/**' | | |
| xargs -0 clang-format --dry-run --Werror | |
| lint: | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: bindings/wasm | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - run: corepack enable | |
| working-directory: . | |
| - uses: actions/setup-node@v7 | |
| with: | |
| node-version: 22 | |
| cache: yarn | |
| cache-dependency-path: | | |
| bindings/wasm/yarn.lock | |
| bindings/node/yarn.lock | |
| - run: yarn install --immutable | |
| - run: yarn lint | |
| - name: Emit the WASM binding's declarations | |
| # The Node type-check reads them: one cross-surface test drives the same | |
| # input through Node, WASM and Python, so it imports the WASM entry | |
| # point. Declarations only -- the emscripten module's types are tracked | |
| # as src/sonare.js.d.ts, so tsc needs no toolchain and dist/sonare.wasm | |
| # is left alone. | |
| run: yarn build:types | |
| - name: Lint and type-check the Node binding | |
| # Neither check needs the native addon built: tsconfig.test.json | |
| # excludes dist/ and build/, and the tests import from ../src rather | |
| # than the package entry point. | |
| working-directory: bindings/node | |
| run: | | |
| yarn install --immutable | |
| yarn lint | |
| yarn test:types | |
| - name: Lint Python | |
| # Pinned to the resolved version in requirements-dev.lock: an unpinned | |
| # install lets a new ruff release fail an unrelated pull request. | |
| # Invoked as a module so the check does not depend on pip's script | |
| # directory being on PATH. | |
| # | |
| # The repo rather than a path list, matching `make lint`. Python lives | |
| # in ten trees here and only the binding was gated, so a lint error | |
| # anywhere else -- `tools/`, `benchmarks/`, `tests/` -- reached develop | |
| # unseen. Ruff respects .gitignore and skips node_modules by default. | |
| working-directory: . | |
| run: | | |
| python3 -m pip install --disable-pip-version-check ruff==0.16.8 | |
| python3 -m ruff check . | |
| - name: Type-check the Python binding | |
| # main-ci type-checks the stubs, but only on main, so type errors used | |
| # to accumulate on develop and surface all at once at release time. | |
| # Source-only like the ruff step: mypy_path resolves libsonare from | |
| # src/, so the package itself is neither installed nor built. numpy is | |
| # needed even though the config skips following it -- the override only | |
| # applies once the module resolves, and without it every importing | |
| # module reports import-not-found. Both are pinned for the reason ruff | |
| # is, and mypy in particular reports new errors on unchanged code with | |
| # every release. | |
| # | |
| # The second invocation is the non-vacuity guard: a file that must not | |
| # type-check, so a misconfiguration that silently checks nothing fails | |
| # here rather than passing everything. | |
| working-directory: bindings/python | |
| run: | | |
| python3 -m pip install --disable-pip-version-check mypy==2.3.1 numpy==2.4.6 | |
| python3 -m mypy --strict src/libsonare ../../tests/typing/python_smoke.py | |
| if python3 -m mypy --strict ../../tests/typing/python_catalog_invalid.py; then | |
| echo "python_catalog_invalid.py unexpectedly passed mypy" >&2 | |
| exit 1 | |
| fi | |
| - name: Drift gates | |
| # The mechanical cross-surface gates, all read-only and stdlib-only | |
| # except numpy: the bank-policy gate imports the shipping | |
| # `tools/voicematch/status.py`, which reaches numpy through `capture.py`. | |
| # Invoked through the Makefile so this list cannot drift from it: a | |
| # hand-copied one silently omits whatever gate is added to `conformance` | |
| # next, and reports green while doing it. | |
| # | |
| # `gen_abi_layout.py --check` stays a direct call, because the Makefile | |
| # wraps it in rye -- which the runner does not have and the script does | |
| # not need. It is the only gate needing a compiler, and it builds a | |
| # small probe from the public C headers rather than the project, so this | |
| # job stays build-free. It is also the load-bearing one: it compares | |
| # every ctypes Structure against sizeof/alignof/offsetof taken from the | |
| # C headers, and a desync there segfaults the Python binding rather than | |
| # failing a test. | |
| # | |
| # `parity` depends on `conformance`, whose closing live-CLI comparison | |
| # skips itself when no built CLI is present, as it is here. | |
| working-directory: . | |
| run: | | |
| python3 -m pip install --disable-pip-version-check numpy==2.4.6 | |
| python3 tools/abi/gen_abi_layout.py --check | |
| make check-abi-version | |
| make processor-types-check | |
| make parity | |
| voicematch: | |
| name: Calibration harness | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Run the calibration harness self-tests | |
| # `tools/` sits outside ctest and outside the drift gates, and this | |
| # workflow runs ctest directly rather than `make test`, so nothing else | |
| # here exercises the harness the instrument bank is calibrated with. | |
| # | |
| # The cases read only the tracked capture definitions and reference | |
| # profiles -- no rendered corpus, no plugin, no built library -- so this | |
| # job stays build-free. Two skip themselves when there is no shared | |
| # library to render with. | |
| # | |
| # Invoked directly rather than through `make test-voicematch`, which | |
| # wraps the same command in rye for a dylib these cases do not need. | |
| # Both versions are the ones resolved in requirements-dev.lock, pinned | |
| # for the reason ruff is: an unpinned install lets a new release fail an | |
| # unrelated pull request. | |
| run: | | |
| python3 -m pip install --disable-pip-version-check pytest==9.1.1 numpy==2.4.6 | |
| python3 -m pytest tools/voicematch tools/audition -q | |
| build-and-test: | |
| name: Native C++ (ubuntu-latest) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y cmake build-essential libeigen3-dev | |
| - name: Configure | |
| run: cmake -B build -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTING=ON -DBUILD_CLI=OFF -DSONARE_WITH_FFMPEG=OFF | |
| - name: Build | |
| # Capped at 2 jobs: unbounded parallelism OOM-kills cc1plus on the | |
| # 4-core/16GB hosted runners when compiling the large Debug test TUs. | |
| run: cmake --build build --parallel 2 | |
| - name: Generate K-weighting reference fixture | |
| run: make fixtures | |
| - name: Test | |
| run: ctest --test-dir build --output-on-failure --parallel | |
| # `make test-golden` is deliberately not run here. The golden hashes | |
| # quantize samples at 1e-6 before folding them into FNV-1a, which is | |
| # finer than the float reproducibility across architectures and libm | |
| # implementations, so a hash generated on one host cannot match another. | |
| # They stay a same-environment regression check, run locally. | |
| - name: Check capability catalog | |
| run: make capability-catalog-check | |
| examples: | |
| name: Examples (Python and C++) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: '3.13' | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y cmake build-essential libeigen3-dev | |
| python -m pip install numpy | |
| - name: Generate a short WAV fixture | |
| run: | | |
| python - <<'PY' | |
| import math | |
| import struct | |
| import wave | |
| with wave.open('example-input.wav', 'wb') as output: | |
| output.setnchannels(1) | |
| output.setsampwidth(2) | |
| output.setframerate(48000) | |
| output.writeframes(b''.join( | |
| struct.pack('<h', round(0.4 * 32767 * math.sin(2 * math.pi * 440 * i / 48000))) | |
| for i in range(48000 * 3) | |
| )) | |
| PY | |
| - name: Build the Python shared library | |
| run: | | |
| cmake -B build-examples -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED=ON \ | |
| -DBUILD_TESTING=OFF -DBUILD_CLI=OFF -DSONARE_WITH_FFMPEG=OFF | |
| cmake --build build-examples --target sonare_shared --parallel 2 | |
| cp build-examples/lib/libsonare.so bindings/python/src/libsonare/ | |
| - name: Run Python examples | |
| env: | |
| PYTHONPATH: bindings/python/src | |
| run: | | |
| python examples/python/analyze.py example-input.wav | |
| python examples/python/master.py example-input.wav example-mastered.wav | |
| python examples/python/midi_to_wav.py example-midi.wav | |
| test -s example-mastered.wav | |
| test -s example-midi.wav | |
| - name: Build and run the C++ example | |
| run: | | |
| cmake -S examples/cpp -B build-examples-cpp -DBUILD_CLI=OFF -DBUILD_TESTING=OFF \ | |
| -DSONARE_WITH_FFMPEG=OFF | |
| cmake --build build-examples-cpp --parallel 2 | |
| build-examples-cpp/analyze example-input.wav | |
| install-package: | |
| # The C++ distribution path, checked the way a consumer meets it: install | |
| # into a scratch prefix, then configure a project that knows nothing but | |
| # find_package(sonare). In-tree builds link every target together, so an | |
| # incomplete link interface or a missing installed header cannot show up | |
| # anywhere else. The macOS half of this runs on the main-branch workflow. | |
| name: Installed package (find_package) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y cmake build-essential libeigen3-dev pkg-config | |
| - name: Build and install into a scratch prefix | |
| run: | | |
| cmake -B build-install -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF \ | |
| -DBUILD_SHARED=ON -DSONARE_WITH_FFMPEG=OFF \ | |
| -DCMAKE_INSTALL_PREFIX="$PWD/install-prefix" | |
| cmake --build build-install --parallel 2 | |
| cmake --install build-install | |
| - name: Consume it from a project that only knows find_package | |
| run: | | |
| cmake -S tests/cmake/consumer -B build-install-consumer \ | |
| -DCMAKE_PREFIX_PATH="$PWD/install-prefix" | |
| cmake --build build-install-consumer --parallel 2 | |
| ctest --test-dir build-install-consumer --output-on-failure --no-tests=error | |
| - name: Check the pkg-config file | |
| run: | | |
| PC_DIR="$(dirname "$(find "$PWD/install-prefix" -name sonare.pc | head -1)")" | |
| PKG_CONFIG_PATH="$PC_DIR" pkg-config --validate sonare | |
| PKG_CONFIG_PATH="$PC_DIR" pkg-config --modversion sonare | |
| PKG_CONFIG_PATH="$PC_DIR" pkg-config --cflags --libs sonare | |
| python-wheel: | |
| name: Python wheel (manylinux x86_64) | |
| runs-on: ubuntu-latest | |
| container: quay.io/pypa/manylinux_2_28_x86_64 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Build, audit, and test wheel | |
| # The manylinux image has no system Eigen package; the top-level CMake | |
| # FetchContent fallback supplies its header-only dependency. | |
| run: | | |
| cmake -B build-wheel -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED=ON \ | |
| -DBUILD_TESTING=OFF -DBUILD_CLI=OFF \ | |
| -DSONARE_WITH_FFMPEG=OFF | |
| cmake --build build-wheel --target sonare_shared --config Release --parallel | |
| cp build-wheel/lib/libsonare.so bindings/python/src/libsonare/ | |
| cd bindings/python | |
| /opt/python/cp313-cp313/bin/python -m pip install wheel hatchling auditwheel pytest | |
| /opt/python/cp313-cp313/bin/python -m pip wheel . --no-deps -w dist-raw/ | |
| auditwheel repair dist-raw/*.whl -w dist/ --plat manylinux_2_28_x86_64 | |
| auditwheel show dist/*.whl | |
| /opt/python/cp313-cp313/bin/python -m pip install dist/*.whl | |
| /opt/python/cp313-cp313/bin/python -c "import libsonare; libsonare.version()" | |
| # The wheel is a shipped artifact, so run its suite against the | |
| # repaired wheel here rather than for the first time at publish time. | |
| # pytest is installed above; `-m "not slow"` comes from pyproject. | |
| /opt/python/cp313-cp313/bin/python -m pytest tests/ -x -q |