Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
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
51 changes: 24 additions & 27 deletions .github/workflows/linux-x64-build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
runs-on: ubuntu-latest
container:
image: ${{ matrix.docker_image }}
continue-on-error: ${{ matrix.experimental == true }}
strategy:
fail-fast: false
matrix:
Expand All @@ -26,6 +27,7 @@ jobs:
- humble
- jazzy
- kilted
- lyrical
- rolling
include:
# Humble Hawksbill (May 2022 - May 2027)
Expand All @@ -37,6 +39,11 @@ jobs:
# Kilted Kaiju (May 2025 - Dec 2026)
- docker_image: ubuntu:noble
ros_distribution: kilted
# Lyrical Luth (May 2026 - May 2031, beta) - allow-failure until packages stabilize
- docker_image: ubuntu:26.04
ros_distribution: lyrical
experimental: true
ros_tar_url: "https://github.com/ros2/ros2/releases/download/release-lyrical-beta-20260429-1/ros2-lyrical-2026-04-29-1-resolute-x86_64.tar.bz2"
# Rolling Ridley (No End-Of-Life) - migrated to Ubuntu 26.04 (resolute)
# to follow the Lyrical Luth target platform.
# See: https://docs.ros.org/en/jazzy/Releases/Release-Lyrical-Luth.html
Expand All @@ -51,40 +58,41 @@ jobs:
architecture: ${{ matrix.architecture }}

- name: Setup ROS2
if: ${{ matrix.ros_distribution != 'rolling' }}
if: ${{ matrix.ros_distribution != 'rolling' && matrix.ros_distribution != 'lyrical' }}
uses: ros-tooling/setup-ros@v0.7
with:
required-ros-distributions: ${{ matrix.ros_distribution }}

- name: Install ROS2 Rolling Nightly
if: ${{ matrix.ros_distribution == 'rolling' }}
- name: Enable ROS2 apt repository (rolling / lyrical)
if: ${{ matrix.ros_distribution == 'rolling' || matrix.ros_distribution == 'lyrical' }}
run: |
apt-get update
apt-get install -y software-properties-common curl

# Enable required repositories (per https://docs.ros.org/en/rolling/Installation/Alternatives/Ubuntu-Install-Binary.html)
# Enable required repositories (per https://docs.ros.org/en/lyrical/Installation/Ubuntu-Install-Debs.html)
add-apt-repository universe
ROS_APT_SOURCE_VERSION=$(curl -s https://api.github.com/repos/ros-infrastructure/ros-apt-source/releases/latest | grep -F "tag_name" | awk -F'"' '{print $4}')
curl -L -o /tmp/ros2-apt-source.deb "https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ROS_APT_SOURCE_VERSION}/ros2-apt-source_${ROS_APT_SOURCE_VERSION}.$(. /etc/os-release && echo ${UBUNTU_CODENAME:-${VERSION_CODENAME}})_all.deb"
dpkg -i /tmp/ros2-apt-source.deb

# Install prerequisites and apt packages BEFORE nightly tarball
apt-get update
apt-get install -y build-essential cmake tar bzip2 python3 python3-rosdep python3-colcon-common-extensions

# Extract nightly binary AFTER apt packages so nightly's newer libs overwrite apt's older ones
curl -sL "${{ matrix.ros_tar_url }}" -o /tmp/ros2-nightly.tar.bz2
mkdir -p /opt/ros/rolling
tar xf /tmp/ros2-nightly.tar.bz2 --strip-components=1 -C /opt/ros/rolling
rm /tmp/ros2-nightly.tar.bz2
- name: Install ROS2 from binary tarball (rolling / lyrical)
if: ${{ matrix.ros_distribution == 'rolling' || matrix.ros_distribution == 'lyrical' }}
run: |
# Extract binary tarball AFTER apt packages so its newer libs overwrite apt's older ones
curl -sL "${{ matrix.ros_tar_url }}" -o /tmp/ros2-${{ matrix.ros_distribution }}.tar.bz2
mkdir -p /opt/ros/${{ matrix.ros_distribution }}
tar xf /tmp/ros2-${{ matrix.ros_distribution }}.tar.bz2 --strip-components=1 -C /opt/ros/${{ matrix.ros_distribution }}
rm /tmp/ros2-${{ matrix.ros_distribution }}.tar.bz2

# Install any remaining runtime dependencies using rosdep
rosdep init || true
rosdep update
rosdep install --rosdistro rolling --from-paths /opt/ros/rolling/share --ignore-src -y --skip-keys "cyclonedds fastcdr fastdds iceoryx_binding_c rmw_connextdds rti-connext-dds-7.3.0 rti-connext-dds-7.7.0 urdfdom_headers python3-pyqt6.qtsvg rosidl_buffer_py pybind11"
rosdep install --rosdistro ${{ matrix.ros_distribution }} --from-paths /opt/ros/${{ matrix.ros_distribution }}/share --ignore-src -y --skip-keys "cyclonedds fastcdr fastdds iceoryx_binding_c rmw_connextdds rti-connext-dds-7.3.0 rti-connext-dds-7.7.0 urdfdom_headers python3-pyqt6.qtsvg rosidl_buffer_py pybind11"

- name: Install test-msgs and mrpt_msgs on Linux
if: ${{ matrix.ros_distribution != 'rolling' }}
if: ${{ matrix.ros_distribution != 'rolling' && matrix.ros_distribution != 'lyrical' }}
run: |
sudo apt install -y ros-${{ matrix.ros_distribution }}-test-msgs ros-${{ matrix.ros_distribution }}-mrpt-msgs

Expand All @@ -101,18 +109,7 @@ jobs:

- uses: actions/checkout@v6

- name: Build and test rclnodejs (nightly)
if: ${{ matrix.ros_distribution == 'rolling' }}
run: |
uname -a
source /opt/ros/rolling/setup.bash
npm i
npm run lint
npm test
npm run clean

- name: Build and test rclnodejs
if: ${{ matrix.ros_distribution != 'rolling' }}
run: |
uname -a
source /opt/ros/${{ matrix.ros_distribution }}/setup.bash
Expand All @@ -121,10 +118,10 @@ jobs:
npm test
npm run clean

- name: Test with IDL ROS messages against rolling
if: ${{ matrix.ros_distribution == 'rolling' }}
- name: Test with IDL ROS messages (rolling / lyrical)
if: ${{ matrix.ros_distribution == 'rolling' || matrix.ros_distribution == 'lyrical' }}
run: |
source /opt/ros/rolling/setup.bash
source /opt/ros/${{ matrix.ros_distribution }}/setup.bash
npm i
npm run test-idl
npm run clean
Expand Down
9 changes: 5 additions & 4 deletions lib/action/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ class ActionClient extends Entity {
};

// Enable feedback subscription content filter optimization.
// Only supported on ROS2 Rolling and only effective when the native
// binding provides the required functions AND the RMW implementation
// actually supports content filtering on the feedback subscription.
// Only supported on ROS2 Lyrical or newer and only effective when the
// native binding provides the required functions AND the RMW
// implementation actually supports content filtering on the feedback
// subscription.
this._enableFeedbackMsgOptimization =
this._options.enableFeedbackMsgOptimization === true &&
DistroUtils.getDistroId() >= DistroUtils.DistroId.ROLLING &&
DistroUtils.getDistroId() >= DistroUtils.DistroId.LYRICAL &&
typeof rclnodejs.actionConfigureFeedbackSubFilterAddGoalId === 'function';

let type = this.typeClass.type();
Expand Down
4 changes: 2 additions & 2 deletions lib/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ class Node extends rclnodejs.ShadowNode {
* @returns {Array} - list of clients
*/
getClientsInfoByService(service, noDemangle = false) {
if (DistroUtils.getDistroId() < DistroUtils.DistroId.ROLLING) {
if (DistroUtils.getDistroId() < DistroUtils.DistroId.LYRICAL) {
console.warn(
'getClientsInfoByService is not supported by this version of ROS 2'
);
Expand Down Expand Up @@ -1621,7 +1621,7 @@ class Node extends rclnodejs.ShadowNode {
* @returns {Array} - list of servers
*/
getServersInfoByService(service, noDemangle = false) {
if (DistroUtils.getDistroId() < DistroUtils.DistroId.ROLLING) {
if (DistroUtils.getDistroId() < DistroUtils.DistroId.LYRICAL) {
console.warn(
'getServersInfoByService is not supported by this version of ROS 2'
);
Expand Down
4 changes: 2 additions & 2 deletions lib/subscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,11 @@ class Subscription extends Entity {

/**
* Check if content filtering is supported for this subscription.
* Requires ROS 2 Rolling or later.
* Requires ROS 2 Lyrical or later.
* @returns {boolean} True if the subscription instance supports content filtering; otherwise false.
*/
isContentFilterSupported() {
if (DistroUtils.getDistroId() < DistroUtils.DistroId.ROLLING) {
if (DistroUtils.getDistroId() < DistroUtils.DistroId.LYRICAL) {
return false;
}
return rclnodejs.isContentFilterSupported(this.handle);
Expand Down
6 changes: 3 additions & 3 deletions rosidl_gen/templates/message-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,12 +266,12 @@ function generateMessage(data) {
const currentTypedArrayElementType = getTypedArrayElementName(spec.baseType);

// ROS 2 Rolling (ros2/rosidl#941) added is_rosidl_buffer / owns_rosidl_buffer
// to every primitive sequence struct. Emit the extra fields only for
// primitive-package types on Rolling+.
// to every primitive sequence struct. Lyrical inherits the same ABI change.
// Emit the extra fields only for primitive-package types on Lyrical+.
const DistroUtils = require('../../lib/distro.js');
const needsRosidlBufferFields =
isPrimitivePackage(spec.baseType) &&
DistroUtils.getDistroId() >= DistroUtils.getDistroId('rolling');
DistroUtils.getDistroId() >= DistroUtils.DistroId.LYRICAL;

// Track required modules
let existedModules = [];
Expand Down
8 changes: 4 additions & 4 deletions src/rcl_action_client_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ Napi::Value ActionSendCancelRequest(const Napi::CallbackInfo& info) {
return Napi::Number::New(env, static_cast<double>(sequence_number));
}

#if ROS_VERSION >= 5000 // ROS2 Rolling
#if ROS_VERSION >= 2605 // ROS2 Lyrical or newer
Napi::Value ActionConfigureFeedbackSubFilterAddGoalId(
const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Expand Down Expand Up @@ -309,7 +309,7 @@ Napi::Value ActionConfigureFeedbackSubFilterRemoveGoalId(

return Napi::Boolean::New(env, true);
}
#endif // ROS_VERSION >= 5000
#endif // ROS_VERSION >= 2605

#if ROS_VERSION >= 2505 // ROS2 >= Kilted
Napi::Value ConfigureActionClientIntrospection(const Napi::CallbackInfo& info) {
Expand Down Expand Up @@ -369,14 +369,14 @@ Napi::Object InitActionClientBindings(Napi::Env env, Napi::Object exports) {
exports.Set("configureActionClientIntrospection",
Napi::Function::New(env, ConfigureActionClientIntrospection));
#endif // ROS_VERSION >= 2505
#if ROS_VERSION >= 5000 // ROS2 Rolling
#if ROS_VERSION >= 2605 // ROS2 Lyrical or newer
exports.Set(
"actionConfigureFeedbackSubFilterAddGoalId",
Napi::Function::New(env, ActionConfigureFeedbackSubFilterAddGoalId));
exports.Set(
"actionConfigureFeedbackSubFilterRemoveGoalId",
Napi::Function::New(env, ActionConfigureFeedbackSubFilterRemoveGoalId));
#endif // ROS_VERSION >= 5000
#endif // ROS_VERSION >= 2605
return exports;
}

Expand Down
2 changes: 1 addition & 1 deletion src/rcl_lifecycle_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Napi::Value CreateLifecycleStateMachine(const Napi::CallbackInfo& info) {
const rosidl_service_type_support_t* gs =
GetServiceTypeSupport("lifecycle_msgs", "GetState");

#if ROS_VERSION >= 5000 // ROS2 Rolling
#if ROS_VERSION >= 2605 // ROS2 Lyrical or newer
rcl_lifecycle_state_machine_options_t options =
rcl_lifecycle_get_default_state_machine_options();
options.enable_com_interface = info[1].As<Napi::Boolean>().Value();
Expand Down
8 changes: 5 additions & 3 deletions test/test-subscription-content-filter.js
Original file line number Diff line number Diff line change
Expand Up @@ -506,9 +506,11 @@ describe('subscription isContentFilterSupported', function () {
const supported = subscription.isContentFilterSupported();
assert.strictEqual(typeof supported, 'boolean');

// isContentFilterSupported requires rolling; on older distros it returns false
const isRolling = DistroUtils.getDistroId() >= DistroUtils.DistroId.ROLLING;
const expectedSupported = isRolling && isContentFilteringSupported();
// isContentFilterSupported requires Lyrical or newer; on older distros it
// returns false.
const isLyricalOrNewer =
DistroUtils.getDistroId() >= DistroUtils.DistroId.LYRICAL;
const expectedSupported = isLyricalOrNewer && isContentFilteringSupported();
assert.strictEqual(supported, expectedSupported);

done();
Expand Down
Loading