build(deps): bump pyo3 from 0.29.0 to 0.29.2 in /ext #162
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: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| permissions: | |
| contents: read | |
| jobs: | |
| test: | |
| name: test (py${{ matrix.python }} / ${{ matrix.os }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python: ["3.12", "3.13", "3.14"] | |
| os: [ubuntu-latest, macos-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| allow-prereleases: true | |
| - name: Install pytest + cryptography (zerotrust optional dep, used by tests) | |
| run: pip install --upgrade pip && pip install pytest cryptography | |
| - name: Run tests | |
| run: PYTHONPATH=lib pytest -q | |
| shellcheck: | |
| name: shellcheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Run shellcheck | |
| run: | | |
| shellcheck hooks/scripts/*.sh | |
| shellcheck install.sh | |
| ascii: | |
| name: ascii check (unicode) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - name: Reject non-ASCII outside the intentional unicode test fixtures | |
| # Enforces CONTRIBUTING hard constraints #2 (ASCII-only) and #3 (no | |
| # em-dashes). git grep -P exits 0 when it finds a match, so a match | |
| # means the build fails. The three fixtures are deliberate unicode. | |
| run: | | |
| if git grep -nP '[^\x00-\x7F]' -- \ | |
| ':!tests/test_model.py' ':!tests/test_redact.py' ':!tests/test_crypto.py'; then | |
| echo "::error::non-ASCII characters found above; the project is ASCII-only outside tests/test_{model,redact,crypto}.py. Use '--' instead of em-dashes." | |
| exit 1 | |
| fi | |
| ruff: | |
| name: ruff (python lint) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: "3.13" | |
| - name: Install ruff | |
| run: pip install --upgrade pip && pip install ruff | |
| - name: Run ruff | |
| run: ruff check lib tests bench | |
| bandit: | |
| name: bandit (python security scan) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: "3.13" | |
| - name: Install bandit | |
| run: pip install --upgrade pip && pip install "bandit[toml]" | |
| - name: Run bandit | |
| run: bandit -c pyproject.toml -r lib | |
| coverage: | |
| name: coverage (statement) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: "3.13" | |
| - name: Install pytest + cryptography + coverage | |
| run: pip install --upgrade pip && pip install pytest cryptography coverage | |
| - name: Run tests under coverage | |
| # In-process measurement (deterministic). The hooks are unit-tested | |
| # in-process; daemon.py is omitted (see pyproject [tool.coverage.run]) | |
| # since it only runs as a subprocess. No COVERAGE_PROCESS_START / combine. | |
| run: PYTHONPATH=lib coverage run -m pytest -q | |
| - name: Report (gate 74%, Silver target 80% tracked in #39) | |
| run: coverage report --fail-under=74 | |
| manifest-integrity: | |
| name: regenerate MANIFEST.lock | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: "3.13" | |
| - name: Regenerate MANIFEST.lock | |
| run: PYTHONPATH=lib python lib/integrity.py --write | |
| - name: Diff MANIFEST.lock | |
| run: | | |
| if ! git diff --exit-code MANIFEST.lock; then | |
| echo "::error::MANIFEST.lock is stale. Run 'PYTHONPATH=lib python lib/integrity.py --write' and commit." | |
| exit 1 | |
| fi | |
| bench: | |
| name: bench (perf regression gate) | |
| runs-on: ubuntu-latest | |
| # Single matrix cell to keep cost down. macOS bench numbers vary too much | |
| # across hosted runners to be useful as a gate. Thresholds set with 2-3x | |
| # headroom over local Python 3.14.4 numbers (cold ~80 ms, aggregate ~6.4 s); | |
| # CI Linux is typically 1.5-2x slower. Bumping the thresholds is a | |
| # deliberate, reviewable change. | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: "3.13" | |
| - name: Install pytest + cryptography | |
| run: pip install --upgrade pip && pip install pytest cryptography | |
| - name: Run cold_startup bench (threshold 300 ms median) | |
| env: | |
| THRESHOLD_MS: "300" | |
| run: | | |
| python3 bench/cold_startup.py --runs 20 | tee /tmp/bench-cold.txt | |
| python3 - <<'PY' | |
| import json, os, pathlib, sys | |
| text = pathlib.Path('/tmp/bench-cold.txt').read_text() | |
| lines = text.splitlines() | |
| start = next(i for i, ln in enumerate(lines) if ln.startswith('{')) | |
| parsed = json.loads('\n'.join(lines[start:])) | |
| median = parsed['summary']['median_ms'] | |
| threshold = float(os.environ['THRESHOLD_MS']) | |
| print(f'cold_startup median_ms = {median} (threshold {threshold})') | |
| if median > threshold: | |
| print(f'::error::cold_startup median {median} exceeded {threshold} ms threshold') | |
| sys.exit(1) | |
| PY | |
| - name: Run aggregate_session bench (threshold 12000 ms median) | |
| env: | |
| THRESHOLD_MS: "12000" | |
| run: | | |
| python3 bench/aggregate_session.py --runs 5 | tee /tmp/bench-agg.txt | |
| python3 - <<'PY' | |
| import json, os, pathlib, sys | |
| text = pathlib.Path('/tmp/bench-agg.txt').read_text() | |
| lines = text.splitlines() | |
| start = next(i for i, ln in enumerate(lines) if ln.startswith('{')) | |
| parsed = json.loads('\n'.join(lines[start:])) | |
| median = parsed['summary']['median_ms'] | |
| threshold = float(os.environ['THRESHOLD_MS']) | |
| print(f'aggregate_session median_ms = {median} (threshold {threshold})') | |
| if median > threshold: | |
| print(f'::error::aggregate_session median {median} exceeded {threshold} ms threshold') | |
| sys.exit(1) | |
| PY | |
| wheel-build: | |
| # Catches pyo3 / pyext-feature-gated dep bumps that the binary build misses. | |
| # Existing CI compiles only `presence-client` with --no-default-features | |
| # (which excludes pyo3); this job compiles the wheel via maturin and smoke- | |
| # tests it. PR #17 (pyo3 0.21 -> 0.24, broken Bound API) green-lit on every | |
| # other job; this is the gate that would have caught it. | |
| name: wheel build (${{ matrix.os }}) | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest] | |
| runs-on: ${{ matrix.os }} | |
| steps: | |
| - uses: actions/checkout@v7.0.1 | |
| - uses: actions/setup-python@v7.0.0 | |
| with: | |
| python-version: "3.13" | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Install Linux system libraries | |
| # Mirrors release.yml; libgit2-sys + secret-service need these on bare | |
| # ubuntu-latest. macOS uses Apple frameworks via the SDK so no apt step. | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| pkg-config libssh2-1-dev libssl-dev zlib1g-dev | |
| - name: Cache cargo registry + ext target | |
| uses: actions/cache@v6.1.0 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| ext/target | |
| key: wheel-build-${{ matrix.os }}-${{ hashFiles('ext/Cargo.lock') }} | |
| - name: Install maturin | |
| run: pip install --upgrade pip && pip install maturin | |
| - name: Build wheel | |
| run: cd ext && maturin build --release --out target/wheels | |
| - name: Smoke-test the wheel | |
| # Install + import + one function call. Catches dynamic-link / ABI | |
| # mismatches that compile time misses. The runner's checkout is a git | |
| # repo so get_head_commit returns a dict; the `is None` allowance | |
| # documents the contract without being brittle. | |
| run: | | |
| WHEEL=$(find ext/target/wheels -name '*.whl' -type f | head -1) | |
| pip install --force-reinstall --break-system-packages "$WHEEL" | |
| python3 -c " | |
| import presence_ext | |
| head = presence_ext.git.get_head_commit('.') | |
| assert head is None or 'sha' in head, f'unexpected get_head_commit shape: {head}' | |
| print('wheel smoke-test ok') | |
| " |