fix(release): prepare immutable 1.5.8 publication #387
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: [master, develop] | |
| pull_request: | |
| branches: [master, develop] | |
| env: | |
| CARGO_HOME: ${{ github.workspace }}/../.cargo-home | |
| CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse | |
| RUST_STABLE_TOOLCHAIN: "1.94.1" | |
| jobs: | |
| # Rust crate: compile, test, lint (parallel with Python jobs) | |
| rust-check: | |
| name: Rust ${{ matrix.os }} | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| matrix: | |
| os: [ubuntu-latest, macos-latest, windows-latest] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13.5" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| components: clippy | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-v3-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| - name: cargo check | |
| working-directory: type-bridge-core | |
| run: cargo check --all-targets | |
| - name: cargo test | |
| working-directory: type-bridge-core | |
| run: cargo test --all-targets | |
| - name: cargo clippy | |
| working-directory: type-bridge-core | |
| run: cargo clippy --all-targets -- -D warnings | |
| # I6 single-feature offline legs: verify neither single-band feature combination rots. | |
| # These are compile-only checks (no live server, no tests) — cargo check only. | |
| band-feature-check: | |
| name: Band feature check (${{ matrix.band }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - band: band7 | |
| features: band7,axum-transport | |
| - band: band8 | |
| features: band8,axum-transport | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-band-v3-${{ matrix.band }}-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| - name: cargo check (${{ matrix.band }}) | |
| working-directory: type-bridge-core | |
| run: cargo check -p type-bridge-orm -p type-bridge-server --no-default-features --features ${{ matrix.features }} | |
| # Rust code coverage (informational, does not block CI) | |
| rust-coverage: | |
| name: Rust Coverage | |
| runs-on: ubuntu-latest | |
| continue-on-error: true | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust nightly | |
| uses: dtolnay/rust-toolchain@5b842231ba77f5c045dba54ac5560fed2db780e2 # nightly | |
| with: | |
| toolchain: nightly | |
| components: llvm-tools-preview | |
| - name: Install cargo-llvm-cov | |
| uses: taiki-e/install-action@fd2f5e3d644b484055ebf4268f474c565f148f25 # v2 | |
| with: | |
| tool: cargo-llvm-cov | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-nightly-v3-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| - name: Generate LCOV report | |
| run: ./scripts/coverage.sh branch --lcov | |
| - name: Upload Rust coverage to Codecov | |
| uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: type-bridge-core/target/llvm-cov/lcov.branch.info | |
| flags: rust | |
| fail_ci_if_error: false | |
| # Build maturin wheels for all platforms (parallel with other jobs) | |
| build-wheels: | |
| name: Build wheel (${{ matrix.target }}) | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - target: x86_64-unknown-linux-gnu | |
| runner: ubuntu-latest | |
| artifact: wheels-linux-x86_64 | |
| - target: aarch64-unknown-linux-gnu | |
| runner: ubuntu-latest | |
| artifact: wheels-linux-aarch64 | |
| - target: x86_64-apple-darwin | |
| runner: macos-latest | |
| artifact: wheels-macos-x86_64 | |
| - target: aarch64-apple-darwin | |
| runner: macos-latest | |
| artifact: wheels-macos-aarch64 | |
| - target: x86_64-pc-windows-msvc | |
| runner: windows-latest | |
| artifact: wheels-windows-x86_64 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up QEMU (Linux aarch64 cross-compilation) | |
| if: matrix.target == 'aarch64-unknown-linux-gnu' | |
| uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 | |
| with: | |
| platforms: arm64 | |
| - name: Set up Python | |
| if: runner.os != 'Linux' | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13.5" | |
| - name: Build wheel | |
| uses: PyO3/maturin-action@e83996d129638aa358a18fbd1dfb82f0b0fb5d3b # v1 | |
| env: | |
| # The manylinux aarch64 cross-compiler does not define the ARM ACLE | |
| # macro __ARM_ARCH that ring's pregenerated assembly requires. The | |
| # variable name is target-suffixed, so it only affects the aarch64 | |
| # Linux wheel even though it is declared on every matrix leg. | |
| CFLAGS_aarch64_unknown_linux_gnu: ${{ matrix.target == 'aarch64-unknown-linux-gnu' && '-D__ARM_ARCH=8' || '' }} | |
| with: | |
| target: ${{ matrix.target }} | |
| rust-toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| args: --release --out dist -i python3.13 | |
| manylinux: auto | |
| working-directory: type-bridge-core | |
| - name: Upload wheel artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ matrix.artifact }} | |
| path: type-bridge-core/dist/*.whl | |
| # Node binding is a required CI gate: the #124 NAPI pillar was previously untested | |
| # in CI, so its native + TypeScript surface and database-free gates run here to keep | |
| # the #110 single-core / thin-bindings invariant from silently regressing. | |
| node-check: | |
| name: Node binding | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: type-bridge-core/crates/node | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-node-v3-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: npm | |
| cache-dependency-path: type-bridge-core/crates/node/package-lock.json | |
| - name: Install Node dependencies | |
| run: npm ci | |
| - name: Build native module and TypeScript surface | |
| run: npm run build | |
| - name: Verify native module built | |
| run: ls type_bridge_node.*.node | |
| - name: Typecheck | |
| run: npm run typecheck | |
| - name: Unified Query contract typecheck | |
| run: npm run typecheck:query-contract | |
| - name: Unit tests | |
| run: npm run test:unit | |
| - name: DTS baseline parity | |
| run: npm run test:dts | |
| - name: Package smoke | |
| run: npm run smoke:package | |
| - name: Legacy packed-package compatibility | |
| run: npm run smoke:legacy-package | |
| # Exercise both legacy and typed Python APIs from the artifacts users receive. | |
| # The native wheel comes from the existing Linux x86_64 build-wheels leg. The | |
| # legacy runner installs the wheels; the typed runner only extracts the same | |
| # prebuilt artifacts and uses an explicitly prepared interpreter/tool pair. | |
| python-legacy-package-compat: | |
| name: Python ${{ matrix.python-version }} installed/built-wheel compatibility | |
| runs-on: ubuntu-latest | |
| needs: build-wheels | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| python-version: ["3.12", "3.13.5", "3.14"] | |
| env: | |
| UV_PYTHON: ${{ matrix.python-version }} | |
| UV_PYTHON_PREFERENCE: only-system | |
| PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1" | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 | |
| with: | |
| enable-cache: true | |
| - name: Download Linux x86_64 core wheel | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: wheels-linux-x86_64 | |
| path: tmp/legacy-python-wheels/core | |
| - name: Build root Python wheel | |
| run: | | |
| uv build --wheel --package type-bridge \ | |
| --out-dir tmp/legacy-python-wheels/root | |
| - name: Prepare typed consumer interpreter and Pyright | |
| run: | | |
| uv venv --python python tmp/typed-python-tools | |
| uv pip install --python tmp/typed-python-tools/bin/python \ | |
| 'pydantic>=2.12.4' 'isodate==0.7.2' 'jinja2>=3.1.0' \ | |
| 'typer>=0.15.0' 'pyright>=1.1.407' | |
| - name: Run isolated installed-package compatibility | |
| run: | | |
| mapfile -t core_wheels < <( | |
| find tmp/legacy-python-wheels/core -maxdepth 1 -type f \ | |
| -name 'type_bridge_core-*.whl' -print | |
| ) | |
| mapfile -t root_wheels < <( | |
| find tmp/legacy-python-wheels/root -maxdepth 1 -type f \ | |
| -name 'type_bridge-*.whl' -print | |
| ) | |
| if [[ ${#core_wheels[@]} -ne 1 ]]; then | |
| printf 'Expected exactly one Linux x86_64 core wheel, found %s\n' \ | |
| "${#core_wheels[@]}" >&2 | |
| exit 1 | |
| fi | |
| if [[ ${#root_wheels[@]} -ne 1 ]]; then | |
| printf 'Expected exactly one root Python wheel, found %s\n' \ | |
| "${#root_wheels[@]}" >&2 | |
| exit 1 | |
| fi | |
| python scripts/ci/run_legacy_python_compat.py \ | |
| --artifact "${core_wheels[0]}" \ | |
| --artifact "${root_wheels[0]}" | |
| python scripts/ci/run_typed_python_artifact.py \ | |
| --root-wheel "${root_wheels[0]}" \ | |
| --core-wheel "${core_wheels[0]}" \ | |
| --python "$PWD/tmp/typed-python-tools/bin/python" \ | |
| --pyright "$PWD/tmp/typed-python-tools/bin/pyright" | |
| # Code quality: linting and formatting (parallel) | |
| lint: | |
| name: Python ${{ matrix.check }} | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| check: [ruff-lint, ruff-format] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13.5" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 | |
| with: | |
| enable-cache: true | |
| - name: Install dependencies | |
| run: uv sync --extra dev | |
| - name: Run ruff lint check | |
| if: matrix.check == 'ruff-lint' | |
| run: uv run ruff check . | |
| - name: Run ruff format check | |
| if: matrix.check == 'ruff-format' | |
| run: uv run ruff format --check . | |
| # Type checking with pyright (parallel matrix for faster execution) | |
| typecheck: | |
| name: Python Type Check (${{ matrix.target }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| target: [type_bridge, examples, tests] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13.5" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 | |
| with: | |
| enable-cache: true | |
| - name: Install dependencies | |
| run: uv sync --extra dev | |
| - name: Install pyright | |
| run: uv pip install pyright | |
| - name: Run pyright on ${{ matrix.target }} | |
| run: uv run pyright ${{ matrix.target }}/ | |
| - name: Check intentional unified Query typing diagnostics | |
| if: matrix.target == 'tests' | |
| run: uv run python tests/contracts/typed_query/python/check_negative.py | |
| # Unit tests (fast, no external dependencies, parallel execution) | |
| test-unit: | |
| name: Python Unit Tests (${{ matrix.python-version }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ["3.12", "3.13.5", "3.14"] | |
| env: | |
| # The repo's .python-version pins 3.13 for local dev; without these, | |
| # uv ignores the matrix interpreter and downloads a managed build | |
| # whose missing shared libpython breaks the typedb native driver. | |
| UV_PYTHON: ${{ matrix.python-version }} | |
| UV_PYTHON_PREFERENCE: only-system | |
| # PyO3 0.23 predates CPython 3.14, but this extension uses abi3-py312. | |
| PYO3_USE_ABI3_FORWARD_COMPATIBILITY: "1" | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python ${{ matrix.python-version }} | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 | |
| with: | |
| enable-cache: true | |
| - name: Install dependencies | |
| run: uv sync --extra dev | |
| - name: Install pytest-xdist for parallel execution | |
| run: uv pip install pytest-xdist | |
| - name: Run unit tests with coverage | |
| run: | | |
| uv run pytest tests/unit/ -v --tb=short -n auto \ | |
| --cov=type_bridge --cov-branch \ | |
| --cov-report=xml:coverage.xml \ | |
| --cov-report=term-missing | |
| - name: Upload Python coverage to Codecov | |
| uses: codecov/codecov-action@0fb7174895f61a3b6b78fc075e0cd60383518dac # v5 | |
| if: always() | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| files: coverage.xml | |
| flags: python | |
| fail_ci_if_error: false | |
| # Integration tests (require TypeDB server, parallel by test category) | |
| test-integration: | |
| name: Python Integration (${{ matrix.test-group }}, ${{ matrix.typedb-server }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| # Every ordinary integration directory is represented here. Parity and | |
| # proxy retain their dedicated jobs because they require extra runtimes. | |
| test-group: | |
| [crud, queries, schema, expressions, migration, session, generator, validation] | |
| # Fan across all four server lines: band-7 (3.8.3, 3.10.4), band-8 | |
| # (3.11.5), and the dual-band 3.12 line. The 3.12 leg prefers the | |
| # embedded band-9 driver after safe discovery, with band-8 retained as | |
| # the compatibility fallback. Keep in sync with compose default and | |
| # version SSOT in crates/core. | |
| typedb-server: | |
| - "typedb/typedb:3.8.3" | |
| - "typedb/typedb:3.10.4" | |
| - "typedb/typedb:3.11.5" | |
| - "typedb/typedb:3.12.0" | |
| include: | |
| # Band-7 legs pair the band-7 installed Python driver — a band-7 user | |
| # has this driver installed, and the gate must not spuriously fire. | |
| - typedb-server: "typedb/typedb:3.8.3" | |
| python-driver: "3.10.0" | |
| - typedb-server: "typedb/typedb:3.10.4" | |
| python-driver: "3.10.0" | |
| - typedb-server: "typedb/typedb:3.11.5" | |
| python-driver: "3.11.5" | |
| # The 3.12 leg pairs the band-9 installed driver a 3.12 user has; | |
| # server 3.12 accepts it natively. | |
| - typedb-server: "typedb/typedb:3.12.0" | |
| python-driver: "3.12.0" | |
| # Start TypeDB as a service container (runs first, before steps) | |
| services: | |
| typedb: | |
| image: ${{ matrix.typedb-server }} | |
| ports: | |
| - 1729:1729 | |
| # TypeDB HTTP API; the connect-time version gate probes /v1/version. | |
| - 8000:8000 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13.5" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 | |
| with: | |
| enable-cache: true | |
| - name: Install dependencies | |
| run: uv sync --extra dev | |
| - name: Install the band's Python driver | |
| run: uv pip install "typedb-driver==${{ matrix.python-driver }}" | |
| - name: Wait for TypeDB server to be ready | |
| run: | | |
| echo "Waiting for TypeDB server to start..." | |
| echo "Container ID: ${{ job.services.typedb.id }}" | |
| # Show initial logs for debugging | |
| echo "=== Initial TypeDB logs ===" | |
| docker logs ${{ job.services.typedb.id }} 2>&1 || echo "Could not fetch logs" | |
| echo "===========================" | |
| # Wait for server to be ready by checking port connectivity | |
| for i in {1..30}; do | |
| if nc -z localhost 1729 2>/dev/null; then | |
| echo "TypeDB server is ready on port 1729!" | |
| docker logs ${{ job.services.typedb.id }} 2>&1 | tail -20 | |
| break | |
| fi | |
| echo "Waiting for port 1729... ($i/30)" | |
| sleep 2 | |
| done | |
| # Final check | |
| if ! nc -z localhost 1729 2>/dev/null; then | |
| echo "ERROR: TypeDB server did not start within 60 seconds" | |
| echo "=== Full TypeDB logs ===" | |
| docker logs ${{ job.services.typedb.id }} 2>&1 | |
| exit 1 | |
| fi | |
| - name: Run integration tests for ${{ matrix.test-group }} | |
| env: | |
| USE_DOCKER: false | |
| TYPEDB_ADDRESS: localhost:1729 | |
| run: uv run pytest tests/integration/${{ matrix.test-group }} -m integration -v --tb=short | |
| # Rust ORM live-TypeDB integration. The ordinary rust-check job deliberately | |
| # leaves `integration-tests` disabled so it remains an offline gate. This job | |
| # selects the non-ignored real-provider target explicitly and mirrors the full | |
| # four-version Python/Node server matrix. | |
| rust-integration: | |
| name: Rust integration (${{ matrix.typedb-server }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| typedb-server: | |
| - "typedb/typedb:3.8.3" | |
| - "typedb/typedb:3.10.4" | |
| - "typedb/typedb:3.11.5" | |
| - "typedb/typedb:3.12.0" | |
| services: | |
| typedb: | |
| image: ${{ matrix.typedb-server }} | |
| ports: | |
| - 1729:1729 | |
| - 8000:8000 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-rust-integration-v1-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| - name: Wait for TypeDB server to be ready | |
| run: | | |
| for i in {1..30}; do | |
| if nc -z localhost 1729 2>/dev/null; then | |
| echo "TypeDB server is ready on port 1729" | |
| break | |
| fi | |
| echo "Waiting for port 1729... ($i/30)" | |
| sleep 2 | |
| done | |
| if ! nc -z localhost 1729 2>/dev/null; then | |
| echo "ERROR: TypeDB server did not start within 60 seconds" | |
| docker logs ${{ job.services.typedb.id }} 2>&1 || true | |
| exit 1 | |
| fi | |
| - name: Run Rust ORM integration suite | |
| env: | |
| TYPEDB_ADDRESS: localhost:1729 | |
| TYPEDB_HTTP_PORT: "8000" | |
| run: >- | |
| cargo test --manifest-path type-bridge-core/Cargo.toml | |
| -p type-bridge-orm --features integration-tests | |
| --test integration -- --nocapture | |
| # Cross-language parity gate: the #110 single-core / thin-bindings invariant only | |
| # holds if a semantic divergence between the Python and Node sides reds CI. These | |
| # tests shell out to the Node binding, so this job builds the Node prerequisites | |
| # first, then runs them against live TypeDB under TYPE_BRIDGE_PARITY_STRICT so a | |
| # "Node not built" skip fails the job instead of passing it silently (false green). | |
| cross-language-parity: | |
| name: Cross-language parity (${{ matrix.typedb-server }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| # Fan across one representative line per protocol band, per I5: | |
| # band-7 (3.8.3), band-8 (3.11.5), and the dual-band 3.12 line. | |
| # 3.10.4 is redundant for a parity proof. | |
| # Keep in sync with compose + the version SSOT. | |
| typedb-server: | |
| - "typedb/typedb:3.8.3" | |
| - "typedb/typedb:3.11.5" | |
| - "typedb/typedb:3.12.0" | |
| include: | |
| # Band-7 leg pairs the band-7 installed Python driver. | |
| - typedb-server: "typedb/typedb:3.8.3" | |
| python-driver: "3.10.0" | |
| - typedb-server: "typedb/typedb:3.11.5" | |
| python-driver: "3.11.5" | |
| - typedb-server: "typedb/typedb:3.12.0" | |
| python-driver: "3.12.0" | |
| services: | |
| typedb: | |
| image: ${{ matrix.typedb-server }} | |
| ports: | |
| - 1729:1729 | |
| # TypeDB HTTP API; the connect-time version gate probes /v1/version. | |
| - 8000:8000 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13.5" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-parity-v2-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 | |
| with: | |
| enable-cache: true | |
| - name: Install dependencies | |
| run: uv sync --extra dev | |
| - name: Install the band's Python driver | |
| run: uv pip install "typedb-driver==${{ matrix.python-driver }}" | |
| - name: Build exact Python parity wheels | |
| run: | | |
| uv build --wheel --package type-bridge-core \ | |
| --out-dir tmp/typed-query-parity-wheels/core | |
| uv build --wheel --package type-bridge \ | |
| --out-dir tmp/typed-query-parity-wheels/root | |
| - name: Export exact Python parity wheel paths | |
| run: | | |
| mapfile -t core_wheels < <( | |
| find tmp/typed-query-parity-wheels/core -maxdepth 1 -type f \ | |
| -name 'type_bridge_core-*.whl' -print | |
| ) | |
| mapfile -t root_wheels < <( | |
| find tmp/typed-query-parity-wheels/root -maxdepth 1 -type f \ | |
| -name 'type_bridge-*.whl' -print | |
| ) | |
| if [[ ${#core_wheels[@]} -ne 1 ]]; then | |
| printf 'Expected exactly one parity core wheel, found %s\n' \ | |
| "${#core_wheels[@]}" >&2 | |
| exit 1 | |
| fi | |
| if [[ ${#root_wheels[@]} -ne 1 ]]; then | |
| printf 'Expected exactly one parity root wheel, found %s\n' \ | |
| "${#root_wheels[@]}" >&2 | |
| exit 1 | |
| fi | |
| printf 'TYPE_BRIDGE_PARITY_CORE_WHEEL=%s\n' \ | |
| "$(realpath "${core_wheels[0]}")" >> "$GITHUB_ENV" | |
| printf 'TYPE_BRIDGE_PARITY_ROOT_WHEEL=%s\n' \ | |
| "$(realpath "${root_wheels[0]}")" >> "$GITHUB_ENV" | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "20" | |
| cache: npm | |
| cache-dependency-path: type-bridge-core/crates/node/package-lock.json | |
| - name: Install Node dependencies | |
| working-directory: type-bridge-core/crates/node | |
| run: npm ci | |
| # build:types is required too: the dynamic parity readers require() the package | |
| # root, which resolves to dist/index.js — build:native only produces the cdylib. | |
| - name: Build Node parity prerequisites | |
| working-directory: type-bridge-core/crates/node | |
| run: | | |
| npm run build:native | |
| npm run build:types | |
| npm run build:typed-integration | |
| npm run build:parity-reader | |
| npm run build:generator-parity | |
| - name: Export native artifact path | |
| working-directory: type-bridge-core/crates/node | |
| run: echo "TYPE_BRIDGE_NODE_NATIVE_PATH=$(ls "$PWD"/type_bridge_node.*.node | head -1)" >> "$GITHUB_ENV" | |
| - name: Wait for TypeDB server to be ready | |
| run: | | |
| for i in {1..30}; do | |
| if nc -z localhost 1729 2>/dev/null; then | |
| echo "TypeDB server is ready on port 1729" | |
| break | |
| fi | |
| echo "Waiting for port 1729... ($i/30)" | |
| sleep 2 | |
| done | |
| if ! nc -z localhost 1729 2>/dev/null; then | |
| echo "ERROR: TypeDB server did not start within 60 seconds" | |
| docker logs ${{ job.services.typedb.id }} 2>&1 || true | |
| exit 1 | |
| fi | |
| - name: Run cross-language parity (strict — skips for an unbuilt Node binding fail) | |
| env: | |
| USE_DOCKER: false | |
| TYPEDB_ADDRESS: localhost:1729 | |
| TYPE_BRIDGE_PARITY_STRICT: "1" | |
| # The `integration or not integration` marker overrides pyproject's default | |
| # `-m 'not integration'` filter so this gate runs BOTH the live-TypeDB parity | |
| # tests and the offline cross-language ones (descriptor snapshots, generator | |
| # parity, fixture contract) — the latter otherwise run in no CI job. | |
| run: | | |
| uv run pytest tests/integration/parity -m "integration or not integration" \ | |
| -ra --tb=short --junitxml=parity-results.xml | |
| - name: Assert parity tests actually ran (guard against zero-collected) | |
| if: always() | |
| run: | | |
| uv run python - <<'PY' | |
| import sys | |
| import xml.etree.ElementTree as ET | |
| tree = ET.parse("parity-results.xml") | |
| root = tree.getroot() | |
| suite = root if root.tag == "testsuite" else root.find("testsuite") | |
| tests = int(suite.get("tests", "0")) | |
| skipped = int(suite.get("skipped", "0")) | |
| ran = tests - skipped | |
| print(f"parity: tests={tests} skipped={skipped} ran={ran}") | |
| if ran <= 0: | |
| print("ERROR: no parity test executed — gate would be a no-op") | |
| sys.exit(1) | |
| PY | |
| # Node binding live-TypeDB integration. `node-check` covers the offline Node gates and | |
| # `cross-language-parity` runs the Python parity suite against the binding — but neither | |
| # runs the binding's OWN integration suites. This job mirrors the Python `test-integration` | |
| # job for Node: a TypeDB service container plus the native build, then the binding's | |
| # `test:integration` suite (which itself chains `test:typed-integration`). | |
| node-integration: | |
| name: Node integration (${{ matrix.typedb-server }}) | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: type-bridge-core/crates/node | |
| strategy: | |
| matrix: | |
| # Fan across all four server lines: band-7 (3.8.3, 3.10.4), band-8 | |
| # (3.11.5), and the dual-band 3.12 line. No python-driver pairing | |
| # needed — Node reaches TypeDB only through the embedded Rust orm seam. | |
| typedb-server: | |
| - "typedb/typedb:3.8.3" | |
| - "typedb/typedb:3.10.4" | |
| - "typedb/typedb:3.11.5" | |
| - "typedb/typedb:3.12.0" | |
| services: | |
| typedb: | |
| image: ${{ matrix.typedb-server }} | |
| ports: | |
| - 1729:1729 | |
| # TypeDB HTTP API; the connect-time version gate probes /v1/version. | |
| - 8000:8000 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-node-v2-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| # Node >= 22 is required here (unlike the other Node jobs): run-integration.js | |
| # executes the .test.ts files directly via node --test, relying on Node's | |
| # built-in type stripping, which Node 20 does not have. | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22" | |
| cache: npm | |
| cache-dependency-path: type-bridge-core/crates/node/package-lock.json | |
| - name: Install Node dependencies | |
| run: npm ci | |
| # Full build (native cdylib + tsc dist/): the integration tests require() the | |
| # package root, which resolves to dist/index.js, so build:native alone is not enough. | |
| - name: Build package | |
| run: npm run build | |
| - name: Export native artifact path | |
| run: echo "TYPE_BRIDGE_NODE_NATIVE_PATH=$(ls "$PWD"/type_bridge_node.*.node | head -1)" >> "$GITHUB_ENV" | |
| - name: Wait for TypeDB server to be ready | |
| run: | | |
| for i in {1..30}; do | |
| if nc -z localhost 1729 2>/dev/null; then | |
| echo "TypeDB server is ready on port 1729" | |
| break | |
| fi | |
| echo "Waiting for port 1729... ($i/30)" | |
| sleep 2 | |
| done | |
| if ! nc -z localhost 1729 2>/dev/null; then | |
| echo "ERROR: TypeDB server did not start within 60 seconds" | |
| docker logs ${{ job.services.typedb.id }} 2>&1 || true | |
| exit 1 | |
| fi | |
| # The Node suites default TYPEDB_ADDRESS to :1730; the service is on :1729, so set it | |
| # explicitly. test:integration runs the plain integration suite then chains | |
| # test:typed-integration, so one command covers the binding's full live-DB surface. | |
| - name: Run Node integration suite | |
| env: | |
| USE_DOCKER: false | |
| TYPEDB_ADDRESS: localhost:1729 | |
| run: npm run test:integration | |
| # Version-gate cells: each cell boots a server and checks either a positive | |
| # connect path or the expected rejection class. | |
| # POS-grpc-fallback-band8: wrong HTTP port still connects through gRPC band 8. | |
| # POS-grpc-fallback-band7: wrong HTTP port tries band 8, then connects through band 7. | |
| # NEG-window: Database.connect() rejects unsupported servers. | |
| # NEG-driver: Database.driver rejects incompatible installed Python drivers. | |
| # NEG-driver-band9: the asymmetric 3.12 pair — a band-9 driver against a | |
| # 3.11 server is rejected, while band-8 against 3.12 is served. | |
| # The band-7 servers (3.8.3, 3.10.4) are now SERVED: normal operation is | |
| # proven green by the test-integration and node-integration legs, while the | |
| # gRPC-only fallback is covered here as an explicit positive cell. Cell | |
| # versions mirror the version SSOT in crates/core and the compatibility table. | |
| version-gate-cells: | |
| name: Version gate (${{ matrix.cell }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - cell: POS-grpc-fallback-band8 | |
| typedb-server: "typedb/typedb:3.11.5" | |
| python-driver: "3.11.5" | |
| probe: connect | |
| http-port: 1 | |
| expect: ok | |
| - cell: POS-grpc-fallback-band7 | |
| typedb-server: "typedb/typedb:3.10.4" | |
| python-driver: "3.10.0" | |
| probe: connect | |
| http-port: 1 | |
| expect: ok | |
| - cell: NEG-window | |
| typedb-server: "typedb/typedb:3.7.3" | |
| python-driver: "3.11.5" | |
| probe: connect | |
| http-port: 8000 | |
| expect: window | |
| - cell: NEG-driver | |
| typedb-server: "typedb/typedb:3.11.5" | |
| python-driver: "3.10.0" | |
| probe: driver | |
| http-port: 8000 | |
| expect: installed | |
| # Asymmetric band acceptance (measured live): a band-9 (3.12) driver | |
| # is refused by a 3.11 server even though the reverse pair — | |
| # band-8 driver against a 3.12 server — is served. | |
| - cell: NEG-driver-band9 | |
| typedb-server: "typedb/typedb:3.11.5" | |
| python-driver: "3.12.0" | |
| probe: driver | |
| http-port: 8000 | |
| expect: installed | |
| services: | |
| typedb: | |
| image: ${{ matrix.typedb-server }} | |
| ports: | |
| - 1729:1729 | |
| # TypeDB HTTP API; the connect-time version gate probes /v1/version. | |
| - 8000:8000 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: "3.13.5" | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable | |
| with: | |
| toolchain: ${{ env.RUST_STABLE_TOOLCHAIN }} | |
| - name: Cache cargo registry and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ${{ env.CARGO_HOME }}/registry | |
| ${{ env.CARGO_HOME }}/git | |
| type-bridge-core/target | |
| key: ${{ runner.os }}-cargo-gate-v2-${{ hashFiles('type-bridge-core/Cargo.lock') }} | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5 | |
| with: | |
| enable-cache: true | |
| - name: Install dependencies | |
| run: uv sync --extra dev | |
| - name: Install the cell's Python driver | |
| run: uv pip install "typedb-driver==${{ matrix.python-driver }}" | |
| - name: Wait for TypeDB server to be ready | |
| run: | | |
| for i in {1..30}; do | |
| if nc -z localhost 1729 2>/dev/null; then | |
| echo "TypeDB ready on 1729" | |
| break | |
| fi | |
| sleep 2 | |
| done | |
| nc -z localhost 1729 | |
| - name: Assert version-gate cell | |
| run: uv run python scripts/ci/assert_gate_rejection.py --address localhost:1729 --probe ${{ matrix.probe }} --http-port ${{ matrix.http-port }} --expect ${{ matrix.expect }} |