Skip to content

ci: Migrate to pixi-based CI with AppImage generation#3

Merged
facontidavide merged 15 commits intomainfrom
unify-backends
Mar 2, 2026
Merged

ci: Migrate to pixi-based CI with AppImage generation#3
facontidavide merged 15 commits intomainfrom
unify-backends

Conversation

@facontidavide
Copy link
Contributor

Summary

  • Replace 3 Docker-based CI workflows (ros-humble, ros-jazzy, ros-rolling) with a single pixi-based ci.yaml
  • Matrix over humble, jazzy, kilted environments using RoboStack conda channels
  • Add experimental AppImage generation job (one per ROS distro), triggered on tags or manual dispatch
  • Drop rolling from release-debs.yaml matrix (robostack-rolling doesn't exist; kilted already present)
  • Bump pre-commit check-added-large-files limit to 1 MB for pixi.lock

Known issue

  • The kilted environment is not yet in pixi.lock — the robostack-kilted channel was returning 503 during development. The lock needs to be regenerated once the channel is back. The kilted CI job will fail until then.

Test plan

  • Verify humble and jazzy CI jobs pass
  • Verify kilted CI job fails with clear lock error (expected until lock is regenerated)
  • Trigger manual dispatch to test AppImage generation
  • Verify release-debs.yaml matrix no longer includes rolling

🤖 Generated with Claude Code

facontidavide and others added 8 commits March 2, 2026 14:07
Architecture: Extract backend-agnostic core into app/ with abstract
interfaces (TopicSourceInterface, SubscriptionManagerInterface).
ROS2 adapter in ros2/, RTI Connext DDS adapter in rti/. BridgeServer
takes interfaces via constructor injection; event loop externalized.

Bug fixes from code review:
- #1: Incremental session update during subscribe prevents ref-count
  leak if client disconnects mid-subscribe (add_subscription rollback)
- #2: Remove failed topics from session on resume to prevent ref-count
  underflow on disconnect
- #4: Unsubscribe now accepts object format [{"name": "/topic"}] in
  addition to string format ["/topic"]
- #6: Bound incoming WebSocket queue to 1024 messages to prevent
  unbounded memory growth under overload
- #7: Release clients_mutex_ before sending in WebSocket middleware
  (send_reply, send_binary, publish_data) to reduce lock contention
- #9: Validate publish_rate > 0 early in initialize(), before starting
  the middleware
- #15: Remove dead last_read_timestamp_ns_ field from MessageBuffer

Test improvements:
- Enhanced MockSubscriptionManager with ref-count tracking and
  underflow detection
- Fixed UnsubscribeRemovesTopics and SubscribeIsAdditive to use real
  mock topics (previously tested wrong thing)
- Added 7 new regression tests for each bug fix

317 tests pass (regular + TSAN + ASAN clean).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 7cd20ad6de03
All plans have been implemented. Remove docs/plans/ directory
and its reference from CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 4eeea9be2ecb
Entire-Checkpoint: c7b9dbe3e69a
Entire-Checkpoint: ecc6040d1f1f
- Add eProsima Fast DDS 3.4 backend (fastdds/) with Conan deps
- Extract shared standalone event loop from RTI/FastDDS main.cpp
  into app/src/standalone_event_loop.cpp (~80 lines deduplication)
- RTI get_participant() returns std::optional instead of throwing
- FastDDS unsubscribe() uses std::optional (matches RTI pattern)
- Remove unused ref_count() from RTI DdsSubscriptionManager
- Add = delete copy/move to ROS2 adapter classes for consistency
- ROS2 shutdown explicitly calls middleware->shutdown()
- Replace FetchContent CLI11 with Conan cli11/2.6.0
- Update CLAUDE.md and ARCHITECTURE.md for three-backend design

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 357d9892151b
- FastDDS: reuse DynamicData + SerializedPayload as listener members
  (eliminates 2 heap allocations per incoming sample on the hot path)
- RTI/FastDDS: split subscribe() lock into 3 phases so DDS entity
  creation doesn't block on_data_available callbacks
- Core: make snapshot_and_reset_stats() consistently reset-on-read
  for all fields (total_bytes_published was cumulative, now reset)
- FastDDS: call delete_contained_entities() before delete_participant()
  for defensive cleanup
- RTI/FastDDS: warn on cross-domain topic name collisions instead of
  silently dropping
- ROS2: remove redundant middleware->shutdown() (BridgeServer dtor
  already handles it)
- ROS2: remove dead code (unused node_ member, TopicDiscovery
  get_topics/refresh/topics_ + 2 tests)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: f870afb66437
Replace ros-humble.yaml, ros-jazzy.yaml, ros-rolling.yaml with a
single ci.yaml that uses pixi environments via RoboStack. Add kilted
environment (replacing rolling). Add experimental AppImage generation
job (one per distro) triggered on tags or manual dispatch. Drop rolling
from release-debs matrix. Bump large-file limit to 1 MB for pixi.lock.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: fca108541517
The robostack-kilted channel is currently returning 503, preventing
lock file resolution. Comment out kilted in pixi.toml and CI matrix
until the channel is available.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: fca108541517
@facontidavide
Copy link
Contributor Author

CI Status

Both humble and jazzy jobs failed due to robostack anaconda.org outage (503 Service Unavailable on all robostack channels). This is an upstream infrastructure issue, not a workflow bug.

The workflow correctly:

  1. Installs pixi
  2. Reads the manifest and lock file
  3. Attempts to download packages from lock

The server returned 503 when fetching packages like ros-humble-lifecycle-0.20.5-...conda.

Action: Re-run CI once robostack channels are back online.

facontidavide and others added 7 commits March 2, 2026 17:13
robostack-kilted channel is back online. Regenerated pixi.lock with
kilted environment. Re-enabled kilted in CI matrix (build-and-test +
appimage). Renamed ci.yaml to pixi_ci.yaml per request.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 56afe6d7342f
Conda/pixi environments don't ship libzstd.a. Fall back to the shared
library and remove ZSTD_STATIC_LIBRARY from required variables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: 56afe6d7342f
- Add ros_ci.yaml: Docker-based colcon CI for humble/jazzy/kilted
- CMakeLists.txt: FetchContent fallback for ixwebsocket only (sole dep
  not in rosdep); spdlog and nlohmann_json use find_package REQUIRED
- package.xml: Add libspdlog-dev, nlohmann-json-dev, zstd-dev as
  build_depend so rosdep installs them for colcon builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: daa251598522
- Add colcon-based build instructions to README
- Update CI badges to point to new workflow files (pixi_ci, ros_ci)
- Fix repo URL in badges (plotjuggler_bridge)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: daa251598522
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: daa251598522
The rosdep key is the package name, not the Ubuntu package name.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: daa251598522
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: daa251598522
@facontidavide facontidavide merged commit 8c9fd26 into main Mar 2, 2026
7 checks passed
@facontidavide facontidavide deleted the unify-backends branch March 2, 2026 17:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant