Skip to content

Merge pull request #371 from ai-2070/renovate/lock-file-maintenance #5873

Merge pull request #371 from ai-2070/renovate/lock-file-maintenance

Merge pull request #371 from ai-2070/renovate/lock-file-maintenance #5873

Workflow file for this run

name: CI
on:
push:
# Branches only — without this filter GitHub fires `push`
# events on tag pushes too, so every `cli-v*` / `deck-v*`
# release tag spawns a duplicate CI run alongside its
# release workflow. The release workflows themselves run
# their own build + tests against the same SHA; we don't
# need CI to repeat them per tag.
branches: ["**"]
paths:
- "net/**"
- "go/**"
- ".github/workflows/ci.yml"
env:
CARGO_TERM_COLOR: always
jobs:
unit-tests:
name: Unit tests
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-unit-${{ hashFiles('**/Cargo.lock') }}
- name: Run unit tests
run: cargo test --lib --features "net redex cortex netdb"
# Doctests live in `///` comment blocks across `src/` and `sdk/src/`.
# `cargo test --lib` does not execute them — they need a separate
# `cargo test --doc` invocation. Same feature set as the unit-test
# run so doc examples gated on those features compile.
- name: Run doctests
if: ${{ !cancelled() }}
run: cargo test --doc --features "net redex cortex netdb"
# `deck` is a separate workspace member (TUI operator console).
# It's a binary-only package — `[[bin]]` with no `[lib]` — so
# `--lib` / `--doc` would fail with "no library targets found".
# Its `#[test]` modules live alongside the binary sources;
# `--bins` compiles them into the binary's test harness.
- name: Run Deck unit tests
if: ${{ !cancelled() }}
run: cargo test -p net-deck --bins
integration-tests:
name: Integration tests (Net, 3-node, Mikoshi, RedEX, CortEX, NetDB, nRPC, Dataforts, MeshDB, MeshOS)
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-integration-${{ hashFiles('**/Cargo.lock') }}
# nextest gives us per-test process isolation, smarter scheduling,
# and `--no-fail-fast` semantics across grouped test binaries.
# Tests below are batched by feature set so cargo recompiles the
# library at most once per group instead of once per `--test` flag.
- name: Install cargo-nextest
uses: taiki-e/install-action@v2
with:
tool: nextest
# redis and jetstream integration tests are intentionally skipped:
# `required-features = ["redis" | "jetstream"]` in Cargo.toml excludes
# them from compilation under this feature set. Wire them in separately
# once CI service containers are added.
# === Bus + FFI (no features) ========================================
- name: Bus + FFI
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast
--test bus_shutdown_drain
--test bus_stranded_flush
--test ffi_shutdown_race
--test ffi_poll_buffer
# === Net mesh + capability + discovery + migration + nRPC dispatch ==
# All --features net tests share one compile.
- name: Net mesh, capability, migration, nRPC dispatch
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features net
--test integration_net
--test three_node_integration
--test channel_auth
--test channel_auth_hardening
--test connect_post_start
--test failure_detector_matrix
--test parallel_bind_stress
--test peer_death_evicts_peer_map
--test send_subprotocol_malformed
--test subnet_enforcement
--test integration_placement_filter_callback
--test capability_broadcast
--test capability_multihop
--test capability_scope
--test capability_auth_call_path
--test capability_auth_conformance
--test capability_schema_doc_guard
--test chain_discovery
--test cross_lang_capability_fixtures
--test migration_integration
--test migration_target_failure_mid_chunking
--test netsocket_production_defaults
--test parsed_packet_short_input
--test proxy_coverage_gaps
--test stream_config_and_error_display
# === NAT traversal ==================================================
- name: NAT traversal
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features "net nat-traversal"
--test connect_direct
--test nat_classify
--test peer_death_clears_capability_index
--test punch_keepalive
--test punch_keepalive_timing
--test reflex_probe
--test reflex_override
--test rendezvous_ack
--test rendezvous_coordinator
# === Port mapping (UPnP-IGD / NAT-PMP / PCP) ========================
- name: Port mapping
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features "net port-mapping"
--test port_mapping_null
--test port_mapping_real_router
# === RedEX ==========================================================
# `redex` chains to `net` in Cargo.toml (redex = ["net", ...]) so a
# single --features redex covers the e2e suite as well.
- name: RedEX
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features redex
--test integration_redex
--test redex_replication_dst
--test redex_replication_e2e
# === CortEX + nRPC service surface ==================================
# `tool` chains to `cortex` (see `tool = ["cortex"]` in
# Cargo.toml), so adding it here is feature-graph-cheap and
# keeps `integration_tool_announce` — the substrate-side
# serve_tool / announce / tool_metadata.fetch contract pin —
# in this group rather than spawning a fourth compile pass.
- name: CortEX + nRPC + AI Tools
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features "cortex tool"
--test integration_cortex_adapter
--test integration_cortex_tasks
--test integration_cortex_memories
--test cortex_ryw_global_cap
--test integration_mesh_cancel
--test integration_nrpc_loopback
--test integration_nrpc_mesh
--test integration_nrpc_streaming
--test integration_nrpc_client_streaming
--test integration_nrpc_duplex
--test integration_nrpc_service_discovery
--test integration_nrpc_predicate_header
--test integration_nrpc_cross_lang
--test integration_nrpc_cross_lang_streaming
--test integration_tool_announce
--test nrpc_inbound_dispatcher
--test queue_group_dispatch
--test aggregator_fold_query
--test aggregator_registry_rpc
# === NetDB ==========================================================
- name: NetDB
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features netdb
--test integration_netdb
--test netdb_builder_and_accessors
# === Dataforts (greedy + gravity) ===================================
- name: Dataforts greedy + gravity
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features dataforts
--test dataforts_greedy_e2e
--test dataforts_gravity_e2e
# === Scheduled-stream batched drain (Linux sendmmsg send path) ======
# The whole NRPC_SEND_LOOP_BATCHING_PLAN gate, in one compile. The
# scheduled-stream egress ships packets via the send loop's batched,
# group-by-destination drain, which calls `sendmmsg` behind
# `cfg(target_os = "linux")` — a block that does not compile on the
# macOS dev host, so this Linux job is the gate that actually compiles
# AND exercises it. `dataforts` pulls in `net`, so all three drain
# tests build under one feature set:
# - send_drain_depth — floods 16 concurrent scheduled streams,
# prints the packets/syscall-collapse factor;
# - scheduled_stream — pins the scheduled-vs-direct routing;
# - scheduled_stream_integrity — fetches blobs over reliable scheduled
# streams and asserts BYTE-FOR-BYTE delivery through the batch path
# (incl. a tiny-send-buffer phase that forces the sendmmsg partial
# -send / EWOULDBLOCK tail), the integrity check the others can't make.
# `--no-capture` surfaces the measurement + verified-blob/flush counts.
- name: Scheduled-stream batched drain (Linux sendmmsg path)
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features dataforts --no-capture
--test send_drain_depth
--test scheduled_stream
--test scheduled_stream_integrity
# === Batched ingress (Linux recvmmsg receive path) ==================
# The NRPC_RECV_LOOP_BATCHING_PLAN gate. The mesh receive loop drains the
# socket via `recvmmsg` (BatchedPacketReceiver) when
# `MeshNodeConfig::with_batched_ingress(true)` is set — a path behind
# `cfg(target_os = "linux")` that does not compile on the macOS dev host,
# so this Linux job is the gate that compiles AND exercises it.
# - batched_ingress_integrity — fetches blobs concurrently into a
# fetcher built with batched ingress and asserts BYTE-FOR-BYTE
# delivery, then that the batched recv path actually carried the bulk
# (`recv_batch_stats().syscalls > 0`) — the assertion that only fires
# on Linux where the recvmmsg path is live.
# - recv_drain_depth — floods a node with fire-and-forget datagrams (no
# reliability, so no retransmit-exhaustion flakiness) and asserts the
# recvmmsg path collapsed syscalls (`packets > syscalls`), printing the
# packets/syscall factor — the heavy-load recv analogue of
# send_drain_depth.
# `--no-capture` surfaces the packets/recvmmsg-batch collapse counts.
- name: Batched ingress (Linux recvmmsg path)
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features dataforts,batched-ingress --no-capture
--test batched_ingress_integrity
--test recv_drain_depth
# === Dataforts blob e2e (multi-node + migration) ====================
# `redex-disk` is required so MeshBlobAdapter can spin up a real
# on-disk RedEX per node — separate from the in-memory dataforts
# group above so the on-disk paths get compiled.
- name: Dataforts blob e2e
if: ${{ !cancelled() }}
run: cargo nextest run --no-fail-fast --features "dataforts redex-disk" --test dataforts_blob_e2e
# === Transport C ABI error-code mirror ==============================
# Parses `include/net_transport.h` and asserts every `#define
# NET_*` matches its Rust `pub const NET_*` sibling. Gated on
# `net + dataforts + netdb + redex-disk` per the test's cfg.
- name: Transport error-code mirror
if: ${{ !cancelled() }}
run: cargo nextest run --no-fail-fast --features "net dataforts netdb redex-disk" --test transport_error_codes
# === Dataforts net-blob CLI =========================================
# Pulls in clap via the `cli` feature; kept separate so the
# non-CLI dataforts groups don't pay the clap compile cost.
- name: Dataforts net-blob CLI
if: ${{ !cancelled() }}
run: cargo nextest run --no-fail-fast --features "dataforts cli" --test net_blob_cli
# === Dataforts blob v0.3 conformance (Tree + CDC + RS) ==============
# Phase-pinned end-to-end conformance tests for the v0.3 blob
# storage track. Each test runs at CI-feasible scale (default
# parameters tuned for the runner's per-test memory budget; the
# 100 GiB real-scale variant in the Tree test is `#[ignore]`'d
# and opt-in via `-- --ignored` for release validators):
#
# - Tree (Phase A8): byte-identical round-trip, determinism,
# manifest LRU effectiveness, manifest-body-explosion guard.
# - CDC (Phase B4): end-to-end determinism, dedup-after-edit
# ratio under one-byte flip + suffix-shift insert.
# - RS (Phase C9): round-trip with all chunks present, m-chunk-
# loss reconstruction, m+1-loss unrecoverable, cross-adapter
# determinism.
- name: Dataforts blob v0.3 conformance
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features dataforts
--test dataforts_blob_v3_tree_conformance
--test dataforts_blob_v3_cdc_conformance
--test dataforts_blob_v3_rs_conformance
--test dataforts_blob_v3_composed_conformance
# === MeshDB =========================================================
# `meshdb` chains to cortex → redex → net, so a single
# --features meshdb covers the test's `feature = "net" + "meshdb"`
# gate.
- name: MeshDB
if: ${{ !cancelled() }}
run: cargo nextest run --no-fail-fast --features meshdb --test meshdb_subprotocol_wire
# === MeshOS + Deck ==================================================
# `meshos` chains to cortex → redex → net. The pipeline test pins
# the contract between MeshOsLoop reconcile → admit →
# ActionDispatcher across phases; the deck test pins the operator
# surface (DeckClient → AdminCommands → in-process MeshOsRuntime)
# that Deck-the-binary composes against. Both share the same
# `meshos` gate and run in one cargo invocation.
- name: MeshOS + Deck
if: ${{ !cancelled() }}
run: >
cargo nextest run --no-fail-fast --features meshos
--test meshos_pipeline
--test deck_pipeline
loom-tests:
name: Loom concurrency models
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
# Loom builds need their own cache key — the `--cfg loom`
# RUSTFLAGS pulls in a different set of dev-deps (`loom`
# itself is `[target.'cfg(loom)'.dev-dependencies]`) and
# rebuilds every atomic-touching crate with loom's
# substitute primitives. Sharing the cache with non-loom
# jobs would just thrash both.
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-loom-${{ hashFiles('**/Cargo.lock') }}
# Loom tests exhaustively explore thread interleavings; release
# mode is mandatory (debug builds are too slow to converge in CI
# time budget). The models in `loom_models.rs` use loom's
# substitute atomics standalone, not the production types, so
# the test only imports from `loom` itself.
#
# `--no-default-features` skips compiling the `net` feature's
# `adapter::net` module: tokio gates `tokio::net::*` out under
# `#![cfg(not(loom))]`, and `adapter::net::{proxy, router,
# transport}` use `tokio::net::UdpSocket` extensively. Trying
# to compile the lib's net stack under `--cfg loom` fails on
# the missing tokio::net module. The loom test itself doesn't
# touch the net stack, so this is purely a build-time gate
# for the lib that the test target implicitly depends on.
- name: Run loom models
run: cargo test --release --no-default-features --test loom_models
env:
RUSTFLAGS: --cfg loom
rust-sdk-tests:
name: Rust SDK tests
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net/sdk
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-sdk-${{ hashFiles('**/Cargo.lock') }}
# Feature set covers every SDK integration test except
# `redis_dedup_contract` (gated on `redis`, deferred until CI
# service containers land — same policy as the main crate's
# `integration_redis`). Without this width, tests gated on
# `cortex` / `testing` / `compute` / `nat-traversal` /
# `port-mapping` / `tool` / `macros` compile to empty test
# binaries and the contract goes silently unverified.
# `cargo test` auto-discovers every `tests/*.rs` file under
# `sdk/tests/`; the step name calls out the load-bearing
# ones so a missing test binary is obvious in the CI log:
# - mesh_stream_backpressure — substrate flow-control pin
# - mesh_rpc_bidi_typed — Phase E client-streaming +
# duplex typed SDK surface (5 tests). Gated on
# `feature = "net" + "cortex"`, both present here.
# - tool_serve_round_trip — Mesh::serve_tool + call_tool +
# watch_tools live two-mesh path. Gated on
# `feature = "tool" + "cortex"`.
# - tool_format_golden_vectors — T-1 byte-equality
# verifier (openai / anthropic / mcp / gemini lowering +
# reverse). Gated on `feature = "tool"`.
# - tool_event_golden_vectors — T-2 ToolEvent envelope
# round-trip verifier. Gated on `feature = "tool"`.
# - tool_macro — `#[tool]` proc-macro
# expansion + serve-helper round-trip. Gated on
# `feature = "macros" + "cortex"`.
- name: Run Rust SDK tests (includes mesh_stream_backpressure, mesh_rpc_bidi_typed, tool_*)
run: cargo test --features "net cortex dataforts testing compute nat-traversal port-mapping aggregator tool macros"
# SDK doctests — `///` examples in `sdk/src/`. Same feature set
# as the test run so doc examples gated on those features
# compile.
- name: Run Rust SDK doctests
if: ${{ !cancelled() }}
run: cargo test --doc --features "net cortex testing compute nat-traversal port-mapping tool macros"
node-tests:
name: Node bindings tests (vitest)
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net/bindings/node
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-node-${{ hashFiles('**/Cargo.lock') }}
- name: Install npm dependencies
run: npm i
# Build napi binding with the features the vitest suite covers:
# `net` for the mesh surface + `cortex` for the CortEX / NetDB
# tests + `compute` for the MeshDaemon / migration tests +
# `groups` for the `ReplicaGroup` / `ForkGroup` / `StandbyGroup`
# tests (Stages 3 + 4 of `SDK_COMPUTE_SURFACE_PLAN` and
# `SDK_GROUPS_SURFACE_PLAN`) + `test-helpers` so
# `_testInjectSyntheticPeer` is exported for the group-placement
# fixtures.
#
# `cortex` transitively enables redex + netdb via the binding's
# feature graph. Skip the `redis` default — the Node suite
# doesn't hit it, and pulling it in would need a service
# container.
#
# Invoke napi directly (not `npm run build:test`) so we can pass
# `--no-default-features` to suppress `redis`; the package.json
# scripts bake `redis` in for consumers who want the full
# surface from source.
- name: Build native module
working-directory: net/crates/net/bindings/node
run: npx napi build --platform --no-default-features --features net,cortex,compute,groups,meshos,deck,meshdb,aggregator,tool,test-helpers
# Compile the sibling .ts shims (meshdb.ts, mesh_rpc.ts, errors.ts)
# into .js so `require('../meshdb')` in the test suite resolves.
# `napi build` only produces the native `.node` artifact; the
# AsyncIterable shim that augments MeshQueryStream is plain TS
# that needs its own tsc pass.
- name: Build TypeScript shims
working-directory: net/crates/net/bindings/node
run: npm run build:ts
- name: Run tests
run: npm test
sdk-ts-tests:
name: TypeScript SDK tests (vitest + tsc)
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net/sdk-ts
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: "24"
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-sdk-ts-${{ hashFiles('**/Cargo.lock') }}
# `sdk-ts/package.json` carries `@net-mesh/core: file:../bindings/node`
# as a devDependency, so `npm i` over there picks up the sibling
# binding from the working tree. The binding's `.node` cdylib has
# to be built first or `npm test` will fail to load it.
#
# Feature flags MUST include `redis` even though the suite never
# opens a Redis connection: `sdk-ts/src/redis-dedup.ts` does a
# real `export { RedisStreamDedup } from '@net-mesh/core'`, and the
# napi-generated `index.d.ts` only carries the `RedisStreamDedup`
# declaration when the binding was built with the `redis`
# feature. Without it, tsc fails the SDK build with
# `Module '"@net-mesh/core"' has no exported member
# 'RedisStreamDedup'`. The runtime side is also satisfied — the
# `.node` cdylib carries the class — but the runtime never opens
# a connection because no SDK code instantiates the helper in
# the test suite.
#
# `test-helpers` is required so `_testInjectSyntheticPeer` is
# exported for the group-placement fixtures —
# `sdk-ts/test/groups.test.ts` uses the same hook the bindings
# suite does.
- name: Build native module
working-directory: net/crates/net/bindings/node
run: |
npm i --ignore-scripts
npx napi build --platform --no-default-features --features redis,net,cortex,compute,groups,meshos,deck,meshdb,aggregator,tool,test-helpers,dataforts
- name: Install npm dependencies
run: npm i
# `tsc` first — the v0.11 `originHash` widening (u32 → u64,
# number → bigint) cascaded through every method signature and
# interface in the SDK's source. Type-check failures here mean
# the SDK's type surface drifted away from the binding's
# generated `.d.ts`; surface them as a CI failure rather than
# only as a `npm run build` failure on a developer workstation.
- name: Type-check (tsc)
run: npm run build
# vitest auto-discovers everything under `test/`. Notable
# deck-specific coverage exercised by this step:
# - test/deck.test.ts — Symbol.asyncDispose hook shape +
# runtime drain. The runtime test promotes the "binding
# stale" skip to a hard failure when CI=true, so a
# misconfigured --features list (missing `deck`) above
# fails loudly rather than silently skipping.
- name: Run vitest
run: npm test
python-tests:
name: Python bindings tests (pytest)
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net/bindings/python
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
# Match `net/crates/net/rust-toolchain.toml` directly so the
# pinned 1.96.0 toolchain is installed up-front rather than
# auto-fetched mid-`maturin develop` by rustup (which has flaked
# — partial download leaves `rustc` absent on the new toolchain).
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.96.0"
components: clippy, rustfmt, llvm-tools-preview
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-python-${{ hashFiles('**/Cargo.lock') }}
# maturin develop requires a virtualenv (refuses to install into
# the system Python). Create one and prepend it to PATH so every
# subsequent step resolves `maturin`, `pip`, `pytest`, and the
# installed `net` extension from the venv.
- name: Create virtualenv
run: |
python -m venv .venv
echo "${PWD}/.venv/bin" >> "$GITHUB_PATH"
echo "VIRTUAL_ENV=${PWD}/.venv" >> "$GITHUB_ENV"
- name: Install maturin and pytest
# pytest-timeout surfaces the offending test on hangs —
# without it, a deadlock anywhere in the suite ticks the
# job-level timeout-minutes and the CI log gives no hint
# which test was stuck. 60 s is generous for unit/integration
# mesh tests on GHA; the longest legitimate test in the
# suite is well under that.
run: pip install --upgrade pip maturin pytest pytest-timeout
# `net,cortex,compute,groups,meshdb,meshos,deck` mirrors the
# Node binding's feature set for tests. `groups` is needed so
# `test_groups.py` can import `ReplicaGroup` / `ForkGroup` /
# `StandbyGroup` / `GroupError`; `meshdb` / `meshos` / `deck`
# enable the corresponding test modules (each guarded by
# `pytest.skip` when the feature is absent) plus the stub-drift
# test's MeshDB class coverage. Default `redis` is omitted
# because the suite doesn't exercise the Redis adapter.
- name: Build and install bindings
run: maturin develop --no-default-features --features net,cortex,compute,groups,meshdb,meshos,deck,aggregator,tool
# `--timeout-method=thread` is critical for this FFI-heavy
# suite: the default `signal` method only works while Python
# is between bytecode instructions, so a deadlock inside a
# pyo3 call (or in a Rust-spawned thread we're waiting on)
# never gets interrupted. Thread-mode wakes a watchdog after
# the deadline regardless of where the test is blocked and
# dumps a full Python + C frame for the hung test.
#
# `--timeout=30` so even if a hang lands 19 min into the
# run, the watchdog still has a minute of budget to dump the
# offender before the 20-minute job ceiling fires.
#
# `-s` (`--capture=no`) disables pytest's stdout/stderr
# capture so timeout dumps + faulthandler traces appear in
# the GHA log live, not buffered behind a hung test's exit.
# `PYTHONFAULTHANDLER=1` enables faulthandler at interpreter
# startup so a hard hang in a Rust FFI call (where the
# pytest-timeout thread can't print through pytest's capture)
# still dumps a C-level stack when SIGSEGV/SIGABRT fires or
# when the watchdog sends the dump signal.
- name: Run tests
env:
PYTHONFAULTHANDLER: "1"
PYTHONUNBUFFERED: "1"
run: pytest -v -s --timeout=30 --timeout-method=thread
sdk-py-tests:
name: Python SDK tests (sdk-py, pytest)
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net/sdk-py
steps:
- uses: actions/checkout@v6
- name: Setup Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
# sdk-py is the pure-Python layer over the `net-mesh` bindings.
# Its `tests/conftest.py` stubs the native `net` extension before
# any SDK module loads, so the test run does not need maturin or
# the Rust toolchain. `--no-deps` skips resolving the runtime
# requirement against PyPI.
- name: Install sdk-py (no deps) + test deps
run: |
pip install --upgrade pip
pip install --no-deps -e .
pip install pytest pydantic
# pytest auto-discovers `tests/` per pyproject.toml's
# `testpaths`. Notable deck-specific coverage:
# - tests/test_deck_wrapper.py — wrapper-level
# DeckClient.from_seed kwarg forwarding +
# __enter__/__exit__ dunder dispatch (stubs the raw
# pyo3 class so this stays a pure-Python test).
- name: Run tests
run: pytest -v
go-tests:
name: Go bindings tests
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
with:
go-version-file: go/go.mod
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-go-${{ hashFiles('**/Cargo.lock') }}
# The Go bindings link against SIX cdylibs via cgo:
# - `-lnet` (libnet.so, the main `net-mesh` crate) from net.go
# - `-lnet_compute` (libnet_compute.so, the `net-compute-ffi`
# workspace member at bindings/go/compute-ffi) from compute.go
# - `-lnet_meshdb` (libnet_meshdb.so, the `net-meshdb-ffi`
# workspace member at bindings/go/meshdb-ffi) from meshdb.go
# - `-lnet_meshos` (libnet_meshos.so, the `net-meshos-ffi`
# workspace member at bindings/go/meshos-ffi) from meshos.go
# - `-lnet_deck` (libnet_deck.so, the `net-deck-ffi`
# workspace member at bindings/go/deck-ffi) from deck.go
# - `-lnet_rpc` (libnet_rpc.so, the `net-rpc-ffi`
# workspace member at bindings/go/rpc-ffi) from
# mesh_rpc.go / mesh_rpc_typed.go / tool.go. Added in the
# NET_GO_NRPC_PORT slices (G-1..G-5) — without it the
# typed/streaming/tool tests fail at link.
# All six end up in `net/crates/net/target/release/`.
#
# Features:
# - `net-mesh` needs `netdb,dataforts,redex-disk,nat-traversal,redis`
# for the mesh + cortex + blob + NAT + redis-dedup FFI surfaces
# exercised by the Go test suite. `redis` only gates the
# `RedisStreamDedup` symbol export — the dedup helper itself is
# an in-memory LRU and does not need a Redis server at runtime.
# - `net-compute-ffi` needs `test-helpers` so the FFI symbol
# `net_compute_test_inject_synthetic_peer` (referenced by
# `groups_testhelpers.go`) is exported.
# - `net-meshdb-ffi`, `net-meshos-ffi`, `net-deck-ffi`,
# `net-rpc-ffi` have no extra features beyond their defaults.
#
# Built as one cargo invocation so feature unification on the
# shared `net-mesh` dep doesn't cause a second compilation pass.
- name: Build net + net_compute + net_meshdb + net_meshos + net_deck + net_rpc cdylibs
working-directory: net/crates/net
run: >
cargo build --release
-p net-mesh -p net-compute-ffi -p net-meshdb-ffi -p net-meshos-ffi -p net-deck-ffi -p net-rpc-ffi
--features "net-mesh/netdb,net-mesh/dataforts,net-mesh/redex-disk,net-mesh/nat-traversal,net-mesh/redis,net-compute-ffi/test-helpers"
# `integration_test.go` gates its suite behind RUN_INTEGRATION_TESTS=1
# so local developers can run quick unit-style passes without the
# full mesh setup. CI wants the full coverage.
- name: Run Go tests
working-directory: go
env:
LD_LIBRARY_PATH: ${{ github.workspace }}/net/crates/net/target/release
RUN_INTEGRATION_TESTS: "1"
run: go test -v ./...
# Run again with `-tags test_helpers` so the test-only files
# (`groups_testhelpers.go`, `groups_test.go`,
# `capability_aggregation_e2e_test.go`) compile and execute.
# These reference FFI symbols gated behind the `test-helpers`
# cargo feature on `net-compute-ffi` (already enabled in the
# build step above) — without the Go build tag they're
# excluded from the package compile and silently skipped.
- name: Run Go tests (test_helpers)
working-directory: go
env:
LD_LIBRARY_PATH: ${{ github.workspace }}/net/crates/net/target/release
RUN_INTEGRATION_TESTS: "1"
run: go test -v -tags test_helpers ./...
clippy:
name: Clippy
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-clippy-${{ hashFiles('**/Cargo.lock') }}
# Stage 4 of PANIC_AUDIT_AND_LINT_HARDENING_PLAN.md: production
# code (lib + bins) is denied for the four panic-hygiene + unsafe
# lints declared in `Cargo.toml`'s `[lints.clippy]` table. This
# step has no `-A` overrides — any new `.unwrap()` /
# `.expect()` / undocumented `unsafe` block / multi-op unsafe
# block in lib / bin code fails CI here. `--all-targets`
# coverage for OTHER clippy lints lives in the next step.
- name: Run clippy (production code, strict)
run: cargo clippy --all-features --lib --bins -- -D warnings
# `--all-targets` adds clippy coverage for tests / benches /
# examples. Tests are allowed to `.unwrap()` / `.expect()` freely
# (~5000 sites across the test surface) and rarely contain
# `unsafe`. The four lints from `[lints.clippy]` are silenced
# here with `-A`. The `clippy::disallowed_methods` rule
# (driven by `clippy.toml`) is silenced inside each test module
# via inner `#![allow(clippy::disallowed_methods)]` attributes
# — Cargo's `[lints]`-table-vs-CLI precedence quirks make a
# blanket `-A` flag here unreliable. Every OTHER clippy lint
# still fails on warning across all targets via `-D warnings`.
- name: Run clippy (all targets, test surface allowed to panic)
run: >
cargo clippy --all-features --all-targets --
-D warnings
-A clippy::unwrap_used
-A clippy::expect_used
-A clippy::undocumented_unsafe_blocks
-A clippy::multiple_unsafe_ops_per_block
# The `clippy` job above lints only the root `net` package — `cargo
# clippy` without `--workspace` doesn't traverse workspace members,
# and the bindings each need a different feature combination so a
# single `--workspace --all-features` invocation isn't workable
# either (the Python and Node bindings carry build-tool-specific
# default features that conflict in CI). This matrix lints each
# FFI / SDK crate with the same feature flags its build / test job
# uses, so a clippy regression in any binding fails CI before it
# lands.
ffi-clippy:
name: FFI clippy (${{ matrix.crate.name }})
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
crate:
- name: rust-sdk
path: net/crates/net/sdk
# `full` enables every SDK feature flag (net, cortex,
# compute, groups, redis, jetstream). Mirrors the
# `rust-sdk-tests` job's surface plus the optional
# adapter features.
features: --features full
- name: node
path: net/crates/net/bindings/node
# Mirrors the `node-tests` napi build (no redis default,
# plus `test-helpers` so the test-only FFI symbols are
# included in the lint set, plus `meshos,deck,meshdb` so
# the MeshOS / Deck / MeshDB NAPI surfaces compile).
features: --no-default-features --features net,cortex,compute,groups,meshos,deck,meshdb,aggregator,tool,test-helpers
- name: python
path: net/crates/net/bindings/python
# Mirrors the `python-tests` `maturin develop` invocation
# (plus `meshos,deck` so the corresponding pyo3 surfaces
# are linted, plus `aggregator` so the aggregator pyo3
# surface is linted in lockstep with the test job).
features: --no-default-features --features net,cortex,compute,groups,meshos,deck,aggregator,tool
- name: go-compute-ffi
path: net/crates/net/bindings/go/compute-ffi
# Default features only — the cdylib production build
# ships without `test-helpers`, and that is the surface
# we want CI to enforce against. The `test-helpers`
# symbols are exercised by the `go test` suite, not by
# consumers.
features: ""
- name: go-rpc-ffi
# nRPC FFI shim — carries the dispatcher trampolines
# (`net_rpc_set_handler_dispatcher`,
# `net_rpc_set_client_streaming_handler_dispatcher`,
# `net_rpc_set_duplex_handler_dispatcher`,
# `net_rpc_set_streaming_handler_dispatcher`) consumed
# by `go/mesh_rpc.go`. Lint-coverage gap closed when the
# streaming-serve dispatcher landed (G-1 of
# NET_GO_NRPC_PORT).
path: net/crates/net/bindings/go/rpc-ffi
features: ""
- name: go-meshdb-ffi
path: net/crates/net/bindings/go/meshdb-ffi
features: ""
- name: go-meshos-ffi
path: net/crates/net/bindings/go/meshos-ffi
features: ""
- name: go-deck-ffi
path: net/crates/net/bindings/go/deck-ffi
features: ""
- name: sdk-macros
# `net-mesh-sdk-macros` ships the `#[tool]` attribute
# proc-macro. Linted separately from the SDK because the
# proc-macro crate doesn't inherit feature flags from
# its consumers — its public surface is the macro's
# generated code, which the SDK's `tool_macro`
# integration test covers separately at runtime.
path: net/crates/net/sdk-macros
features: ""
- name: cli
path: net/crates/net/cli
# `net-cli` pulls the SDK in with the explicit feature
# set its Cargo.toml declares; clippy honors that.
# `--all-targets` covers the bin + integration test
# crates under `tests/`.
features: ""
- name: deck
path: net/crates/net/deck
# The deck TUI binary's `unit-tests` step at the top
# of this workflow runs its `#[cfg(test)] mod tests`
# blocks, but clippy lints + module-level dead-code
# checks weren't covered until this matrix entry.
# Default features only — matches the cargo invocation
# the test step uses, so what the binary ships gets
# linted (not the `demo` feature, which is dev-only).
features: ""
- name: aggregator-daemon
path: net/crates/net/aggregator-daemon
# `net-aggregator-daemon` is a lib + bin crate that
# hosts an AggregatorRegistry + the
# `aggregator.registry` RPC service. `--all-targets`
# covers the bin, lib, and the integration test crate
# under `tests/`. Default features — the crate's
# Cargo.toml pins its SDK feature set.
features: ""
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-clippy-ffi-${{ matrix.crate.name }}-${{ hashFiles('**/Cargo.lock') }}
- name: Run clippy
working-directory: ${{ matrix.crate.path }}
run: cargo clippy ${{ matrix.crate.features }} --all-targets -- -D warnings
ffi-tests:
# Executes `cargo test --lib` for each FFI shim crate. The
# `ffi-clippy` matrix above lints these but never runs their
# internal `#[cfg(test)] mod tests {}` blocks; pre-fix, ~85
# Rust unit tests in `bindings/go/*-ffi` and ~32 in the
# node/python NAPI/PyO3 surfaces were silently uncovered. The
# `rust-sdk` member is omitted — the dedicated `rust-sdk-tests`
# job already covers its tests with a broader feature set.
#
# Notable deck-specific coverage in `go-deck-ffi`:
# - ice_simulate_unknown_variant_leaves_husk_retryable
# - ice_commit_unknown_variant_leaves_husk_retryable
# (both use a thread-local fault-injection switch on
# `build_core_proposal` to exercise the failure-branch
# invariant — the husk stays retry-able when the variant
# is unrecognised)
# - audit_query_setters_set_last_error_on_null_query
# (pins the last-error envelope on every NULL-arm return)
name: FFI tests (${{ matrix.crate.name }})
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
crate:
- name: node
path: net/crates/net/bindings/node
features: --no-default-features --features net,cortex,compute,groups,meshos,deck,aggregator,tool,test-helpers
- name: python
path: net/crates/net/bindings/python
features: --no-default-features --features net,cortex,compute,groups,meshos,deck,aggregator,tool
- name: go-compute-ffi
path: net/crates/net/bindings/go/compute-ffi
features: ""
- name: go-rpc-ffi
path: net/crates/net/bindings/go/rpc-ffi
features: ""
- name: go-meshdb-ffi
path: net/crates/net/bindings/go/meshdb-ffi
features: ""
- name: go-meshos-ffi
path: net/crates/net/bindings/go/meshos-ffi
features: ""
- name: go-deck-ffi
path: net/crates/net/bindings/go/deck-ffi
features: ""
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-test-ffi-${{ matrix.crate.name }}-${{ hashFiles('**/Cargo.lock') }}
# napi-rs links `napi_*` runtime symbols at load time (Node
# provides them inside its embedded V8); a standalone
# `cargo test --lib` binary can't resolve them at link time.
# Lazy-linking flags (`-z lazy`, `--allow-shlib-undefined`,
# `LD_BIND_NOT=1`) defer resolution but modern Ubuntu's
# `-z now` hardening still trips before main() and the
# binary fails with `undefined symbol: napi_get_reference_value`
# / exit 127.
#
# Hard fix: build a tiny `libnapi_stub.so` that exports every
# napi symbol the binding references as a no-op, then
# `LD_PRELOAD` it before running cargo test. The dynamic
# linker finds the symbol in the stub, the process starts,
# and the tests run their pure-Rust helpers
# (`bigint_u64`, capability conversion, etc.) without ever
# invoking the stubs at runtime — none of these tests reach
# the napi-rs runtime code path.
#
# Other matrix members don't need this and would silently
# mask real link-time bugs if the stub were set globally.
- name: Build napi stub library
if: matrix.crate.name == 'node'
working-directory: ${{ matrix.crate.path }}
run: |
cat > /tmp/napi_stub.c <<'CEOF'
// No-op stubs for every napi_* symbol the napi-rs runtime
// references. The cargo-test binary calls none of these at
// runtime — the pure-Rust unit tests this matrix entry
// covers don't exercise napi-rs's JS-bridge code path —
// but the dynamic linker needs symbol *definitions* to load
// the binary at all. Every stub returns 0 (napi_ok) so
// even if a stub IS called by accident, the caller sees a
// benign "success with no value" result.
#include <stddef.h>
typedef int napi_status;
#define NAPI_STUB(name) napi_status name() { return 0; }
NAPI_STUB(napi_get_reference_value)
NAPI_STUB(napi_create_reference)
NAPI_STUB(napi_delete_reference)
NAPI_STUB(napi_reference_ref)
NAPI_STUB(napi_reference_unref)
NAPI_STUB(napi_module_register)
NAPI_STUB(napi_set_instance_data)
NAPI_STUB(napi_get_instance_data)
NAPI_STUB(napi_create_function)
NAPI_STUB(napi_throw)
NAPI_STUB(napi_throw_error)
NAPI_STUB(napi_throw_type_error)
NAPI_STUB(napi_get_undefined)
NAPI_STUB(napi_get_null)
NAPI_STUB(napi_get_global)
NAPI_STUB(napi_create_object)
NAPI_STUB(napi_create_array)
NAPI_STUB(napi_create_array_with_length)
NAPI_STUB(napi_create_string_utf8)
NAPI_STUB(napi_create_string_latin1)
NAPI_STUB(napi_create_string_utf16)
NAPI_STUB(napi_create_double)
NAPI_STUB(napi_create_int32)
NAPI_STUB(napi_create_uint32)
NAPI_STUB(napi_create_int64)
NAPI_STUB(napi_create_bigint_int64)
NAPI_STUB(napi_create_bigint_uint64)
NAPI_STUB(napi_create_bigint_words)
NAPI_STUB(napi_get_value_bigint_int64)
NAPI_STUB(napi_get_value_bigint_uint64)
NAPI_STUB(napi_get_value_bigint_words)
NAPI_STUB(napi_get_value_bool)
NAPI_STUB(napi_get_value_double)
NAPI_STUB(napi_get_value_int32)
NAPI_STUB(napi_get_value_uint32)
NAPI_STUB(napi_get_value_int64)
NAPI_STUB(napi_get_value_string_utf8)
NAPI_STUB(napi_get_value_string_latin1)
NAPI_STUB(napi_get_value_string_utf16)
NAPI_STUB(napi_get_boolean)
NAPI_STUB(napi_typeof)
NAPI_STUB(napi_get_property)
NAPI_STUB(napi_set_property)
NAPI_STUB(napi_has_property)
NAPI_STUB(napi_delete_property)
NAPI_STUB(napi_define_class)
NAPI_STUB(napi_define_properties)
NAPI_STUB(napi_get_property_names)
NAPI_STUB(napi_get_all_property_names)
NAPI_STUB(napi_call_function)
NAPI_STUB(napi_new_instance)
NAPI_STUB(napi_instanceof)
NAPI_STUB(napi_is_array)
NAPI_STUB(napi_get_array_length)
NAPI_STUB(napi_get_element)
NAPI_STUB(napi_set_element)
NAPI_STUB(napi_create_buffer)
NAPI_STUB(napi_create_buffer_copy)
NAPI_STUB(napi_create_external_buffer)
NAPI_STUB(napi_get_buffer_info)
NAPI_STUB(napi_is_buffer)
NAPI_STUB(napi_create_arraybuffer)
NAPI_STUB(napi_create_external_arraybuffer)
NAPI_STUB(napi_get_arraybuffer_info)
NAPI_STUB(napi_is_arraybuffer)
NAPI_STUB(napi_wrap)
NAPI_STUB(napi_unwrap)
NAPI_STUB(napi_remove_wrap)
NAPI_STUB(napi_create_error)
NAPI_STUB(napi_create_type_error)
NAPI_STUB(napi_create_range_error)
NAPI_STUB(napi_get_and_clear_last_exception)
NAPI_STUB(napi_is_exception_pending)
NAPI_STUB(napi_open_handle_scope)
NAPI_STUB(napi_close_handle_scope)
NAPI_STUB(napi_open_escapable_handle_scope)
NAPI_STUB(napi_close_escapable_handle_scope)
NAPI_STUB(napi_escape_handle)
NAPI_STUB(napi_run_script)
NAPI_STUB(napi_get_node_version)
NAPI_STUB(napi_get_version)
NAPI_STUB(napi_create_promise)
NAPI_STUB(napi_resolve_deferred)
NAPI_STUB(napi_reject_deferred)
NAPI_STUB(napi_is_promise)
NAPI_STUB(napi_get_uv_event_loop)
NAPI_STUB(napi_create_threadsafe_function)
NAPI_STUB(napi_get_threadsafe_function_context)
NAPI_STUB(napi_call_threadsafe_function)
NAPI_STUB(napi_acquire_threadsafe_function)
NAPI_STUB(napi_release_threadsafe_function)
NAPI_STUB(napi_ref_threadsafe_function)
NAPI_STUB(napi_unref_threadsafe_function)
NAPI_STUB(napi_add_env_cleanup_hook)
NAPI_STUB(napi_remove_env_cleanup_hook)
NAPI_STUB(napi_add_async_cleanup_hook)
NAPI_STUB(napi_remove_async_cleanup_hook)
NAPI_STUB(napi_fatal_error)
NAPI_STUB(napi_fatal_exception)
NAPI_STUB(napi_async_init)
NAPI_STUB(napi_async_destroy)
NAPI_STUB(napi_make_callback)
NAPI_STUB(napi_create_async_work)
NAPI_STUB(napi_delete_async_work)
NAPI_STUB(napi_queue_async_work)
NAPI_STUB(napi_cancel_async_work)
NAPI_STUB(napi_open_callback_scope)
NAPI_STUB(napi_close_callback_scope)
NAPI_STUB(napi_create_date)
NAPI_STUB(napi_get_date_value)
NAPI_STUB(napi_is_date)
NAPI_STUB(napi_object_freeze)
NAPI_STUB(napi_object_seal)
NAPI_STUB(napi_strict_equals)
NAPI_STUB(napi_coerce_to_bool)
NAPI_STUB(napi_coerce_to_number)
NAPI_STUB(napi_coerce_to_string)
NAPI_STUB(napi_coerce_to_object)
NAPI_STUB(napi_is_error)
NAPI_STUB(napi_get_named_property)
NAPI_STUB(napi_set_named_property)
NAPI_STUB(napi_has_named_property)
NAPI_STUB(napi_get_prototype)
NAPI_STUB(napi_get_cb_info)
NAPI_STUB(napi_get_new_target)
NAPI_STUB(napi_add_finalizer)
NAPI_STUB(napi_create_typedarray)
NAPI_STUB(napi_is_typedarray)
NAPI_STUB(napi_get_typedarray_info)
NAPI_STUB(napi_create_dataview)
NAPI_STUB(napi_is_dataview)
NAPI_STUB(napi_get_dataview_info)
NAPI_STUB(napi_create_symbol)
NAPI_STUB(napi_check_object_type_tag)
NAPI_STUB(napi_type_tag_object)
NAPI_STUB(node_api_get_module_file_name)
NAPI_STUB(node_api_create_external_string_latin1)
NAPI_STUB(node_api_create_external_string_utf16)
NAPI_STUB(node_api_symbol_for)
NAPI_STUB(node_api_create_property_key_latin1)
NAPI_STUB(node_api_create_property_key_utf16)
NAPI_STUB(node_api_create_property_key_utf8)
NAPI_STUB(node_api_create_buffer_from_arraybuffer)
CEOF
gcc -shared -fPIC -o /tmp/libnapi_stub.so /tmp/napi_stub.c
- name: Run tests (node — napi symbols stubbed)
if: matrix.crate.name == 'node'
working-directory: ${{ matrix.crate.path }}
env:
RUSTFLAGS: "-C link-arg=-Wl,--unresolved-symbols=ignore-all -C link-arg=-Wl,--allow-shlib-undefined"
LD_PRELOAD: "/tmp/libnapi_stub.so"
run: cargo test --lib ${{ matrix.crate.features }}
- name: Run tests
if: matrix.crate.name != 'node'
working-directory: ${{ matrix.crate.path }}
run: cargo test --lib ${{ matrix.crate.features }}
cli-tests:
# `net-cli`'s test surface is broader than the FFI shims:
# unit tests inside `src/`, plus integration-test crates under
# `tests/` that spawn the binary via `assert_cmd`. The crate
# has no lib target, so `cargo test --lib` would silently skip
# everything; `cargo test -p net-cli` runs every target cargo
# auto-discovers, so a new `tests/foo.rs` doesn't need a CI
# edit.
#
# Current surface (informational; not a check):
#
# src/main.rs — humantime duration parser
# src/output.rs — format resolver
# src/error.rs — exit-code discriminator pin
# src/parsers.rs — parse_u64_flexible
# src/commands/ice.rs — confirmation-gate logic
# src/commands/identity.rs — iso8601 formatter
# tests/help.rs — help-text + version surface
# tests/exit_codes.rs — documented exit codes
# tests/dry_run.rs — admin verb `--dry-run` envelope
name: Net CLI tests
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-cli-${{ hashFiles('**/Cargo.lock') }}
# Toolchains for the typegen downstream type-checks (E-2 / E-3). Without
# these, tests/typegen_downstream_{ts,python}.rs self-skip; installing
# them here makes those checks actually run.
- name: Install Node + TypeScript
uses: actions/setup-node@v6
with:
node-version: "24"
- name: Install tsc
run: npm install -g typescript@6
- name: Install Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Install mypy + pydantic
run: python -m pip install --upgrade mypy pydantic
- name: Run net-cli tests
run: cargo test -p net-cli
aggregator-daemon-tests:
# `net-aggregator-daemon` is a lib + bin crate. The lib hosts
# the `boot` / `run` entry points + config parsing + template
# validation; the bin is a thin `Cli::parse + run` shim. Unit
# tests live in `src/lib.rs`'s `#[cfg(test)] mod tests`;
# integration tests live in `tests/boot_and_query.rs` and
# drive a real two-node mesh handshake against the daemon's
# registry-RPC surface. `cargo test -p net-aggregator-daemon`
# picks up every target automatically — new `tests/foo.rs`
# entries don't need a CI edit.
name: Aggregator daemon tests
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-aggregator-daemon-${{ hashFiles('**/Cargo.lock') }}
- name: Run net-aggregator-daemon tests
run: cargo test -p net-aggregator-daemon
fmt:
name: Format
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Check formatting
run: cargo fmt --all -- --check
doc:
name: Documentation
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
working-directory: net/crates/net
steps:
- uses: actions/checkout@v6
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo
uses: actions/cache@v5
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
net/crates/net/target/
key: ${{ runner.os }}-cargo-doc-${{ hashFiles('**/Cargo.lock') }}
- name: Build documentation
run: cargo doc --no-deps --all-features
env:
RUSTDOCFLAGS: -D warnings
# `cargo doc --all-features` only docs the current package
# (the root `net-mesh` crate). Workspace members live in
# their own crates; `net-cli` and `deck` both have
# substantial module-level docstrings that document the
# subcommand / TUI surface contracts — keep them
# warning-free.
- name: Build net-cli documentation
run: cargo doc -p net-cli --no-deps
env:
RUSTDOCFLAGS: -D warnings
- name: Build deck documentation
run: cargo doc -p net-deck --no-deps
env:
RUSTDOCFLAGS: -D warnings
- name: Build net-aggregator-daemon documentation
run: cargo doc -p net-aggregator-daemon --no-deps
env:
RUSTDOCFLAGS: -D warnings
# `net-mesh-sdk` is a substantial workspace member with most
# of the user-facing AI tool / nrpc / mesh / netdb docstrings.
# Pinning `--features full` so the full doc surface (tool +
# macros + nrpc + cortex + compute + groups + meshos + deck
# + dataforts + meshdb + aggregator + local + nat-traversal
# + redis + jetstream) is verified warning-free in lockstep
# with the `ffi-clippy` `rust-sdk` lint job.
- name: Build net-mesh-sdk documentation
run: cargo doc -p net-mesh-sdk --no-deps --features full
env:
RUSTDOCFLAGS: -D warnings
# `#[tool]` proc macro crate. User-facing macro docs live
# here; the SDK depends on them via `pub use net_sdk_macros::tool;`.
- name: Build net-mesh-sdk-macros documentation
run: cargo doc -p net-mesh-sdk-macros --no-deps
env:
RUSTDOCFLAGS: -D warnings
# `net-rpc-ffi` is the cgo-facing nRPC shim. Its public
# surface (extern "C" fn signatures + ABI version constants
# + handle types) is what `go/mesh_rpc.go` documents against;
# keep its rustdoc warning-free so the cross-language ABI
# contract notes stay buildable.
- name: Build net-rpc-ffi documentation
run: cargo doc -p net-rpc-ffi --no-deps
env:
RUSTDOCFLAGS: -D warnings