Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
ddea309
Add snap packaging
MirkoFerrati Jul 7, 2026
86c716f
ci: add Snap Store publish job to snap workflow
MirkoFerrati Jul 7, 2026
09d2b1f
fix: resolve snap filename before passing to action-publish (no glob …
MirkoFerrati Jul 7, 2026
ba268a9
ci: build snap on every PR, publish only on tag push
MirkoFerrati Jul 7, 2026
3f9a491
ci: align snap workflow with PlotJuggler (action-build, fetch-depth 0…
MirkoFerrati Jul 7, 2026
0edce30
build(snap): derive version and grade from git tags via adopt-info
MirkoFerrati Jul 7, 2026
e9ca527
chore: gitignore built *.snap artifacts
MirkoFerrati Jul 7, 2026
f4da3ac
ci: run full integration test against built snap; scope release permi…
MirkoFerrati Jul 7, 2026
ec5318e
fix: drop DOWNLOAD_EXTRACT_TIMESTAMP from IXWebSocket FetchContent
MirkoFerrati Jul 7, 2026
7519ede
ci: install minimal ROS 2 deps for integration test instead of setup-ros
MirkoFerrati Jul 7, 2026
ee5fb28
build(snap): drop redundant shared-memory plug
MirkoFerrati Jul 7, 2026
bd353ae
use ros2-jazzy extension
MirkoFerrati Jul 17, 2026
6b9ef2c
build(snap): drop packages now resolved by ros2-jazzy extension + rosdep
MirkoFerrati Jul 17, 2026
abaf730
Apply suggestions from code review
MirkoFerrati Jul 17, 2026
71f5a39
ci(snap): publish tags to jazzy/candidate; add manual promote to jazz…
MirkoFerrati Jul 17, 2026
3ec971b
ci(snap): use canonical/action-build@v1.3.0 instead of snapcore/actio…
MirkoFerrati Jul 17, 2026
7709658
address PR review: reusable snap workflows, ros2cli test, trim comments
MirkoFerrati Oct 8, 2026
70cae15
Merge pull request #2 from MirkoFerrati/mirko/snap_packaging-review
MirkoFerrati Oct 12, 2026
9bc2f9b
Update permissions in snap.yaml workflow
MirkoFerrati Oct 12, 2026
617f99a
reduce the test to a simple check for the websocket, remove leftover …
MirkoFerrati Oct 14, 2026
f00423b
Update README.md
Guillaumebeuzeboc Oct 16, 2026
00de68a
comments from the pr
Guillaumebeuzeboc Oct 16, 2026
e8f48ae
ci: retry websocket subscribe
Guillaumebeuzeboc Oct 16, 2026
ff22de7
feat: add ROS 2 messages for the strictly confined snap
Guillaumebeuzeboc Oct 19, 2026
8e7401b
fix: update snapcraft description
Guillaumebeuzeboc Oct 19, 2026
8d330f4
ci: build on ARM as well the CI
Guillaumebeuzeboc Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/snap-promote.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# =============================================================================
# Snap Promote — manual gate: jazzy/candidate -> jazzy/stable
#
# Run via the Actions tab once a candidate revision has soaked.
# Uses the canonical reusable promote workflow.
# =============================================================================

name: Snap Promote

on:
workflow_dispatch:

permissions: {}

jobs:
promote:
permissions:
contents: read
uses: canonical/robotics-actions-workflows/.github/workflows/promote.yaml@main
secrets:
snapstore-login: ${{ secrets.SNAPSTORE_LOGIN }}
with:
from-channel: jazzy/candidate
to-channel: jazzy/stable
81 changes: 81 additions & 0 deletions .github/workflows/snap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# =============================================================================
# Snap Package — build, test and publish via the canonical reusable workflow.
#
# Branch push -> jazzy/edge, tag push -> jazzy/candidate. PRs build+test only.
# Promotion jazzy/candidate -> jazzy/stable lives in snap-promote.yaml.
#
# SETUP: store the credentials from
# snapcraft export-login --snaps pj-bridge
# as the SNAPSTORE_LOGIN repository secret.
# =============================================================================

name: Snap Package

on:
push:
branches: [main]
tags: ['*']
pull_request:
workflow_dispatch:

permissions: {}

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
snap:
permissions:
contents: read
actions: write
uses: canonical/robotics-actions-workflows/.github/workflows/snap.yaml@main
secrets:
snapstore-login: ${{ secrets.SNAPSTORE_LOGIN }}
with:
runs-on: '["ubuntu-latest", "ubuntu-24.04-arm"]'
snap-track: jazzy
# Packaging check: start the bridge snap, have a ros2-cli snap publish a
# topic, drive one WebSocket subscribe, and confirm the bridge served it.
snap-test-script: |
#!/usr/bin/env bash
set -euxo pipefail

sudo snap install ros2-cli --channel jazzy
# websocat has no snap; grab the pinned static musl binary for this runner.
case "$(uname -m)" in
x86_64) websocat_arch=x86_64 ;;
aarch64) websocat_arch=aarch64 ;;
*) echo "Unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac
sudo curl -fsSL -o /usr/local/bin/websocat \
"https://github.com/vi/websocat/releases/download/v1.13.0/websocat.${websocat_arch}-unknown-linux-musl"
sudo chmod +x /usr/local/bin/websocat

pj-bridge.pj-bridge-ros2 --ros-args -p port:=9090 \
> /tmp/pj_bridge.log 2>&1 &
BRIDGE_PID=$!

ros2-cli.ros2 topic pub -r 1 /hello_world std_msgs/msg/String \
"data: 'hello world'" > /tmp/pub_hello.log 2>&1 &
PUB_PID=$!

# Retry while the WebSocket starts up and DDS discovers the topic.
for attempt in {1..10}; do
echo '{"command":"subscribe","topics":["/hello_world"],"id":"s1"}' \
| timeout 5 websocat ws://localhost:9090 || true
if grep -q "subscribed to topic '/hello_world'" /tmp/pj_bridge.log; then
break
fi
sleep 1
done

set +e
grep -q "subscribed to topic '/hello_world'" /tmp/pj_bridge.log
TEST_EXIT=$?
set -e

kill "${BRIDGE_PID}" "${PUB_PID}" 2>/dev/null || true
echo "--- Bridge log ---"; cat /tmp/pj_bridge.log
echo "--- Publisher log ---"; cat /tmp/pub_hello.log
exit "${TEST_EXIT}"
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,6 @@ install_tsan/
build_asan/
install_asan/
log/

# Built snap artifacts
*.snap
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ pixi global install pj-bridge-ros2-humble \
pj_bridge_ros2 --ros-args -p port:=9090
```

### Snap

Pre-built snaps are available from the [Snap Store](https://snapcraft.io/pj-bridge).

```bash
sudo snap install pj-bridge

# Run
pj-bridge.pj-bridge-ros2 --ros-args -p port:=9090
```

The snap bundles ROS 2 Jazzy and uses UDP-only DDS transport automatically.
No extra ROS 2 installation is required on the machine running the bridge.

### AppImage

Pre-built AppImages are available from [GitHub Releases](https://github.com/PlotJuggler/plotjuggler_bridge/releases).
Expand Down
5 changes: 5 additions & 0 deletions snap/local/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.14)
project(pj_bridge_snap)

find_package(ament_cmake REQUIRED)
ament_package()
17 changes: 17 additions & 0 deletions snap/local/fastrtps_no_shm.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<profiles xmlns="http://www.eprosima.com/XMLSchemas/fastRTPS_Profiles">
<transport_descriptors>
<transport_descriptor>
<transport_id>UDPv4Transport</transport_id>
<type>UDPv4</type>
</transport_descriptor>
</transport_descriptors>
<participant profile_name="default_xrce_dds_profile" is_default_profile="true">
<rtps>
<userTransports>
<transport_id>UDPv4Transport</transport_id>
</userTransports>
<useBuiltinTransports>false</useBuiltinTransports>
</rtps>
</participant>
</profiles>
143 changes: 143 additions & 0 deletions snap/local/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>pj_bridge_snap</name>
<version>0.0.0</version>
<description>Additional runtime middleware and interfaces for the pj-bridge snap</description>
<maintainer email="canonical-robotics-brand@canonical.com">ubuntu-robotics</maintainer>
<license>AGPL-3.0</license>

<buildtool_depend>ament_cmake</buildtool_depend>

<!-- Open-source RMW providers. -->
<exec_depend>rmw_cyclonedds_cpp</exec_depend>
<exec_depend>rmw_fastrtps_cpp</exec_depend>

<exec_depend>ackermann_msgs</exec_depend>
<exec_depend>action_msgs</exec_depend>
<exec_depend>actionlib_msgs</exec_depend>
<exec_depend>automotive_autonomy_msgs</exec_depend>
<exec_depend>automotive_navigation_msgs</exec_depend>
<exec_depend>automotive_platform_msgs</exec_depend>
<exec_depend>autoware_auto_msgs</exec_depend>
<exec_depend>can_msgs</exec_depend>
<exec_depend>cartographer_ros_msgs</exec_depend>
<exec_depend>cascade_lifecycle_msgs</exec_depend>
<exec_depend>control_msgs</exec_depend>
<exec_depend>controller_manager_msgs</exec_depend>
<exec_depend>depthai_ros_msgs</exec_depend>
<exec_depend>diagnostic_msgs</exec_depend>
<exec_depend>dwb_msgs</exec_depend>
<exec_depend>dynamixel_workbench_msgs</exec_depend>
<exec_depend>four_wheel_steering_msgs</exec_depend>
<exec_depend>foxglove_msgs</exec_depend>
<exec_depend>gazebo_msgs</exec_depend>
<exec_depend>geographic_msgs</exec_depend>
<exec_depend>geometry_msgs</exec_depend>
<exec_depend>gps_msgs</exec_depend>
<exec_depend>graph_msgs</exec_depend>
<exec_depend>grasping_msgs</exec_depend>
<exec_depend>grbl_msgs</exec_depend>
<exec_depend>grid_map_msgs</exec_depend>
<exec_depend>irobot_create_msgs</exec_depend>
<exec_depend>leo_msgs</exec_depend>
<exec_depend>lgsvl_msgs</exec_depend>
<exec_depend>lifecycle_msgs</exec_depend>
<exec_depend>map_msgs</exec_depend>
<exec_depend>marti_can_msgs</exec_depend>
<exec_depend>marti_common_msgs</exec_depend>
<exec_depend>marti_dbw_msgs</exec_depend>
<exec_depend>marti_introspection_msgs</exec_depend>
<exec_depend>marti_nav_msgs</exec_depend>
<exec_depend>marti_perception_msgs</exec_depend>
<exec_depend>marti_sensor_msgs</exec_depend>
<exec_depend>marti_status_msgs</exec_depend>
<exec_depend>marti_visualization_msgs</exec_depend>
<exec_depend>mavros_msgs</exec_depend>
<exec_depend>micro_ros_diagnostic_msgs</exec_depend>
<exec_depend>micro_ros_msgs</exec_depend>
<exec_depend>microstrain_inertial_msgs</exec_depend>
<exec_depend>moveit_msgs</exec_depend>
<exec_depend>mrpt_msgs</exec_depend>
<exec_depend>nao_command_msgs</exec_depend>
<exec_depend>nao_sensor_msgs</exec_depend>
<exec_depend>nav_2d_msgs</exec_depend>
<exec_depend>nav_msgs</exec_depend>
<exec_depend>nav2_msgs</exec_depend>
<exec_depend>nmea_msgs</exec_depend>
<exec_depend>novatel_gps_msgs</exec_depend>
<exec_depend>object_recognition_msgs</exec_depend>
<exec_depend>octomap_msgs</exec_depend>
<exec_depend>pal_statistics_msgs</exec_depend>
<exec_depend>pcl_msgs</exec_depend>
<exec_depend>pendulum_msgs</exec_depend>
<exec_depend>phidgets_msgs</exec_depend>
<exec_depend>plansys2_msgs</exec_depend>
<exec_depend>plotjuggler_msgs</exec_depend>
<exec_depend>radar_msgs</exec_depend>
<exec_depend>rc_common_msgs</exec_depend>
<exec_depend>rc_reason_msgs</exec_depend>
<exec_depend>rclpy_message_converter_msgs</exec_depend>
<exec_depend>rcss3d_agent_msgs</exec_depend>
<exec_depend>realsense2_camera_msgs</exec_depend>
<exec_depend>rmf_api_msgs</exec_depend>
<exec_depend>rmf_building_map_msgs</exec_depend>
<exec_depend>rmf_charger_msgs</exec_depend>
<exec_depend>rmf_dispenser_msgs</exec_depend>
<exec_depend>rmf_door_msgs</exec_depend>
<exec_depend>rmf_fleet_msgs</exec_depend>
<exec_depend>rmf_ingestor_msgs</exec_depend>
<exec_depend>rmf_lift_msgs</exec_depend>
<exec_depend>rmf_obstacle_msgs</exec_depend>
<exec_depend>rmf_scheduler_msgs</exec_depend>
<exec_depend>rmf_site_map_msgs</exec_depend>
<exec_depend>rmf_task_msgs</exec_depend>
<exec_depend>rmf_traffic_msgs</exec_depend>
<exec_depend>rmf_visualization_msgs</exec_depend>
<exec_depend>rmf_workcell_msgs</exec_depend>
<exec_depend>robot_calibration_msgs</exec_depend>
<exec_depend>rosapi_msgs</exec_depend>
<exec_depend>rosbridge_msgs</exec_depend>
<exec_depend>rosgraph_msgs</exec_depend>
<exec_depend>rviz_2d_overlay_msgs</exec_depend>
<exec_depend>sensor_msgs</exec_depend>
<exec_depend>shape_msgs</exec_depend>
<exec_depend>smacc2_msgs</exec_depend>
<exec_depend>soccer_vision_2d_msgs</exec_depend>
<exec_depend>soccer_vision_3d_msgs</exec_depend>
<exec_depend>soccer_vision_attribute_msgs</exec_depend>
<exec_depend>statistics_msgs</exec_depend>
<exec_depend>std_msgs</exec_depend>
<exec_depend>stereo_msgs</exec_depend>
<exec_depend>system_modes_msgs</exec_depend>
<exec_depend>teleop_tools_msgs</exec_depend>
<exec_depend>tf2_msgs</exec_depend>
<exec_depend>trajectory_msgs</exec_depend>
<exec_depend>turtlebot3_msgs</exec_depend>
<exec_depend>ublox_msgs</exec_depend>
<exec_depend>ublox_ubx_msgs</exec_depend>
<exec_depend>udp_msgs</exec_depend>
<exec_depend>unique_identifier_msgs</exec_depend>
<exec_depend>ur_dashboard_msgs</exec_depend>
<exec_depend>ur_msgs</exec_depend>
<exec_depend>urg_node_msgs</exec_depend>
<exec_depend>velodyne_msgs</exec_depend>
<exec_depend>vision_msgs</exec_depend>
<exec_depend>vision_msgs_layers</exec_depend>
<exec_depend>visualization_msgs</exec_depend>
<exec_depend>webots_ros2_msgs</exec_depend>
<exec_depend>wiimote_msgs</exec_depend>

<!-- Additional interfaces. -->
<exec_depend>builtin_interfaces</exec_depend>
<exec_depend>composition_interfaces</exec_depend>
<exec_depend>example_interfaces</exec_depend>
<exec_depend>rcl_interfaces</exec_depend>
<exec_depend>ros_gz_interfaces</exec_depend>
<exec_depend>rosbag2_interfaces</exec_depend>
<exec_depend>type_description_interfaces</exec_depend>

<export>
<build_type>ament_cmake</build_type>
</export>
</package>
73 changes: 73 additions & 0 deletions snap/snapcraft.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: pj-bridge
adopt-info: pj_bridge # version and grade derived from git tags (see pj_bridge part)
version: git
title: PlotJuggler Bridge
summary: ROS2 to WebSocket bridge server — forwards topic content at 50 Hz
license: AGPL-3.0
contact: https://github.com/PlotJuggler/plotjuggler_bridge/issues
issues: https://github.com/PlotJuggler/plotjuggler_bridge/issues
source-code: https://github.com/PlotJuggler/plotjuggler_bridge
website: https://github.com/PlotJuggler/plotjuggler_bridge
description: >
ROS 2 bridge server that forwards middleware topic content over WebSocket to
PlotJuggler clients and any other WebSocket consumer.

Clients subscribe to topics and receive ZSTD-compressed aggregated messages at
50 Hz without needing a full middleware installation.

Call the snap with:
pj-bridge.pj-bridge-ros2 --ros-args -p port:=9090

confinement: strict
base: core24

# ROS 2 typesupport and middleware libraries are loaded via dlopen at runtime
# by the ROS 2 plugin system; the library linter cannot detect this.
lint:
ignore:
- library

parts:
pj_bridge:
plugin: colcon
source: .
override-pull: |
craftctl default
# Version/grade from git tag: clean tag -> stable, ahead of tag -> devel.
git config --global --add safe.directory '*'
version="$(git describe --always --tags | sed -e 's/^v//;s/-/+git/;y/-/./')"
if echo "$version" | grep -q '+git'; then grade=devel; else grade=stable; fi
craftctl set version="$version"
craftctl set grade="$grade"
build-packages:
- git
- libssl-dev
- zlib1g-dev

snap-dependencies:
# A separate manifest keeps the broad runtime dependencies (msgs & rmw)
plugin: colcon
source: snap/local
after: [pj_bridge]
colcon-packages: [pj_bridge_snap]
stage:
- -opt/ros/snap/local_setup.sh
- -opt/ros/snap/setup.sh

fastrtps-config:
plugin: dump
source: snap/local
organize:
fastrtps_no_shm.xml: etc/pj-bridge/fastrtps_no_shm.xml
stage:
- etc/pj-bridge/fastrtps_no_shm.xml

apps:
pj-bridge-ros2:
command: opt/ros/snap/lib/pj_bridge/pj_bridge_ros2
extensions: [ros2-jazzy]
plugs:
- network
- network-bind
environment:
FASTRTPS_DEFAULT_PROFILES_FILE: $SNAP/etc/pj-bridge/fastrtps_no_shm.xml
Loading