Skip to content

feat: generic nix native-module builder for cachix - #3151

Merged
paul-nechifor merged 25 commits into
mainfrom
jeff/feat/cachix
Aug 28, 2026
Merged

feat: generic nix native-module builder for cachix#3151
paul-nechifor merged 25 commits into
mainfrom
jeff/feat/cachix

Conversation

@jeff-hykin

@jeff-hykin jeff-hykin commented Jul 23, 2026

Copy link
Copy Markdown
Member

Fix cachix, simplify CI

Which required a couple other things

ref DIM-1285

@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
4882 2 4880 82
View the full list of 2 ❄️ flaky test(s)
dimos.e2e_tests.test_manipulation_planning_groups::test_dual_arm_plans_and_dispatches_both_arms_through_control_coordinator

Flake rate in main: 100.00% (Passed 0 times, Failed 3 times)

Stack Traces | 120s run time
lcm_spy = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1ac8da0>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x7bfbd06bcf40>

    def test_dual_arm_plans_and_dispatches_both_arms_through_control_coordinator(
        lcm_spy: LcmSpy,
        start_blueprint: Callable[..., DimosCliCall],
    ) -> None:
        """Plan one generated plan over both arms and dispatch through one trajectory task."""
>       _start_openarm_mock_planner(start_blueprint, lcm_spy)

lcm_spy    = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1ac8da0>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x7bfbd06bcf40>

dimos/e2e_tests/test_manipulation_planning_groups.py:190: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/e2e_tests/test_manipulation_planning_groups.py:151: in _start_openarm_mock_planner
    lcm_spy.wait_for_saved_topic(JOINT_STATE_TOPIC, timeout=120.0)
        lcm_spy    = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1ac8da0>
        start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x7bfbd06bcf40>
dimos/e2e_tests/lcm_spy.py:97: in wait_for_saved_topic
    wait_until(
        condition  = <function LcmSpy.wait_for_saved_topic.<locals>.condition at 0x7bfbd06bd4e0>
        self       = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1ac8da0>
        timeout    = 120.0
        topic      = '/coordinator_joint_state#sensor_msgs.JointState'
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

predicate = <function LcmSpy.wait_for_saved_topic.<locals>.condition at 0x7bfbd06bd4e0>

    def wait_until(
        predicate: Callable[[], bool],
        *,
        timeout: float,
        interval: float = 0.1,
        message: str | None = None,
    ) -> None:
        """Poll ``predicate`` until it returns truthy or ``timeout`` elapses."""
        deadline = time.monotonic() + timeout
        while time.monotonic() < deadline:
            if predicate():
                return
            time.sleep(interval)
>       raise TimeoutError(message or f"Timed out after {timeout}s waiting for condition")
E       TimeoutError: Timeout waiting for topic /coordinator_joint_state#sensor_msgs.JointState

deadline   = 4399252.104891942
interval   = 0.1
message    = 'Timeout waiting for topic /coordinator_joint_state#sensor_msgs.JointState'
predicate  = <function LcmSpy.wait_for_saved_topic.<locals>.condition at 0x7bfbd06bd4e0>
timeout    = 120.0

.../utils/testing/waiting.py:35: TimeoutError
dimos.e2e_tests.test_manipulation_planning_groups::test_single_arm_plans_and_executes_through_control_coordinator

Flake rate in main: 25.00% (Passed 12 times, Failed 4 times)

Stack Traces | 120s run time
lcm_spy = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1de1be0>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x7bfbd06bcae0>

    def test_single_arm_plans_and_executes_through_control_coordinator(
        lcm_spy: LcmSpy,
        start_blueprint: Callable[..., DimosCliCall],
    ) -> None:
        """Plan with one arm and execute through its trajectory task."""
>       _start_openarm_mock_planner(start_blueprint, lcm_spy)

lcm_spy    = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1de1be0>
start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x7bfbd06bcae0>

dimos/e2e_tests/test_manipulation_planning_groups.py:159: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/e2e_tests/test_manipulation_planning_groups.py:151: in _start_openarm_mock_planner
    lcm_spy.wait_for_saved_topic(JOINT_STATE_TOPIC, timeout=120.0)
        lcm_spy    = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1de1be0>
        start_blueprint = <function start_blueprint.<locals>.set_name_and_start at 0x7bfbd06bcae0>
dimos/e2e_tests/lcm_spy.py:97: in wait_for_saved_topic
    wait_until(
        condition  = <function LcmSpy.wait_for_saved_topic.<locals>.condition at 0x7bfbd065bec0>
        self       = <dimos.e2e_tests.lcm_spy.LcmSpy object at 0x7bfbd1de1be0>
        timeout    = 120.0
        topic      = '/coordinator_joint_state#sensor_msgs.JointState'
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

predicate = <function LcmSpy.wait_for_saved_topic.<locals>.condition at 0x7bfbd065bec0>

    def wait_until(
        predicate: Callable[[], bool],
        *,
        timeout: float,
        interval: float = 0.1,
        message: str | None = None,
    ) -> None:
        """Poll ``predicate`` until it returns truthy or ``timeout`` elapses."""
        deadline = time.monotonic() + timeout
        while time.monotonic() < deadline:
            if predicate():
                return
            time.sleep(interval)
>       raise TimeoutError(message or f"Timed out after {timeout}s waiting for condition")
E       TimeoutError: Timeout waiting for topic /coordinator_joint_state#sensor_msgs.JointState

deadline   = 4399131.907577119
interval   = 0.1
message    = 'Timeout waiting for topic /coordinator_joint_state#sensor_msgs.JointState'
predicate  = <function LcmSpy.wait_for_saved_topic.<locals>.condition at 0x7bfbd065bec0>
timeout    = 120.0

.../utils/testing/waiting.py:35: TimeoutError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

The cmu-nav-natives-marker job was a hand-rolled hashFiles cache layered on
top of Cachix's own content-addressing. Its key drifted whenever `nix build`
dropped a `result` symlink into the hashed tree, so the marker never hit and
the privileged Cachix-deploy job rebuilt + re-pushed on every unrelated PR
(#2733). Nix already computes each module's store path purely from its inputs,
so an unchanged module substitutes from Cachix (no compile) and pushes nothing.

Collapse marker + cmu-nav-natives into one `cachix-build` job running
bin/build-native-modules.
@greptile-apps

greptile-apps Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Introduces a generic Nix native-module builder and updates CI to build, verify, publish, and consume native artifacts through Cachix.

  • Adds AST-based native-module discovery, input hashing, parallel builds, and Cachix publication verification.
  • Gates Cachix publishing with a content-derived cache marker and coordinates dependent test jobs.
  • Runs Rust checks inside each crate’s Nix development shell where available.
  • Updates the virtual MID-360 and voxel ray-tracing flakes and lock files.
  • Extends local native-test setup and adds builder guard-rail tests.

Confidence Score: 5/5

The PR appears safe to merge because no new blocking failure eligible for this follow-up review remains.

No blocking failure remains within the scope of the displayed previous review threads.

Files Needing Attention: No files require blocking changes.

T-Rex T-Rex Logs

What T-Rex did

  • I observed a marker transition from ca33ae… to 412d1f… between the before and after captures, with the manifest reflecting updated virtual Mid360 and ray-tracing input trees.
  • I ran the focused isolated test suite and recorded 5 passed, 1 deselected in 1.56s.
  • I performed determinism checks and confirmed both executions and both byte comparisons exited with code 0.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
bin/build-native-modules Adds native-module discovery, deterministic input manifests, parallel Nix builds, dry-run support, and Cachix verification.
.github/workflows/ci.yml Adds Cachix build gating and publication while coordinating native artifacts with Rust and Python test jobs.
dimos/core/test_build_native_modules.py Adds structural tests for module discovery, input coverage, lock-file handling, and manifest determinism.
dimos/hardware/sensors/lidar/virtual_mid360/flake.nix Switches shared Rust sources to a locked repository input and adds a Rust development shell.
dimos/mapping/ray_tracing/rust/flake.nix Adds a development shell and lock-driven Cargo dependency vendoring for voxel ray tracing.

Reviews (9): Last reviewed commit: "Merge branch 'main' into jeff/feat/cachi..." | Re-trigger Greptile

Comment thread bin/build-native-modules Outdated
Comment thread bin/build-native-modules Outdated
The rust job now builds crates the same way they ship: crates with a
flake run fmt/clippy/test inside `nix develop path:.` (so tests get any
flake-provided deps), flakeless crates keep the plain toolchain, and the
PyO3 extension crates are auto-detected (extension-module in Cargo.toml)
and maturin-built only so pytest can import them. Adds a read-only Cachix
substituter (no auth token, stays unprivileged) + fetch-depth: 0 for
ray_tracing's git+file self-reference.

Also fixes two latent module-flake breakages the generic builder now
exercises: pointlio switches its fast-lio input from git+ssh to github
(public repo, no runner ssh key), and ray_tracing links its macOS pyo3
cdylib with -undefined dynamic_lookup. Both rust flakes gain a
toolchain-only devShells.default so `nix develop` skips the heavy src
derivation (virtual_mid360's relative path: input can't resolve in the
store otherwise).
Comment thread bin/build-native-modules Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread dimos/hardware/sensors/lidar/virtual_mid360/flake.nix Outdated
Comment thread dimos/mapping/ray_tracing/rust/flake.nix Outdated
The per-module stamp tried to skip unchanged rebuilds by comparing a
timestamp against the newest file under build_dir, but that scan misses
deleted/renamed inputs and any input outside build_dir (shared rust
crates, common lidar headers, flake inputs) — so it could report a module
current and reuse a stale executable. It also never helped on self-hosted,
where `git clean -ffdx` wipes the untracked stamp every run. Nix already
content-hashes all inputs and Cachix makes an unchanged closure
near-instant, so just run the build unconditionally.
Comment thread .github/workflows/ci.yml Outdated
@Dreamsorcerer
Dreamsorcerer marked this pull request as ready for review August 26, 2026 15:29
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml Outdated
Dreamsorcerer and others added 2 commits August 26, 2026 17:09
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Aug 26, 2026
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Aug 28, 2026
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Aug 28, 2026
@Dreamsorcerer
Dreamsorcerer added this pull request to the merge queue Aug 28, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 28, 2026
@paul-nechifor
paul-nechifor added this pull request to the merge queue Aug 28, 2026
Merged via the queue into main with commit fc85c9a Aug 28, 2026
30 of 32 checks passed
@paul-nechifor
paul-nechifor deleted the jeff/feat/cachix branch August 28, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants