Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
44 changes: 42 additions & 2 deletions .github/workflows/linux-arm64-build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- humble
- jazzy
- kilted
- lyrical
include:
# Humble Hawksbill (May 2022 - May 2027)
- docker_image: ubuntu:jammy
Expand All @@ -36,6 +37,13 @@ jobs:
# Kilted Kaiju (May 2025 - Dec 2026)
- docker_image: ubuntu:noble
ros_distribution: kilted
# Lyrical Luth (May 2026 - May 2031, beta)
# NOTE: the tarball URL below is a date-stamped beta snapshot and must
# be refreshed manually until upstream publishes a stable "latest"
# download (or until GA).
- docker_image: ubuntu:26.04
ros_distribution: lyrical
ros_tar_url: "https://github.com/ros2/ros2/releases/download/release-lyrical-beta-20260430/ros2-lyrical-2026-04-30-resolute-aarch64.tar.bz2"

steps:
- name: Setup Node.js ${{ matrix.node-version }} on ${{ matrix.architecture }}
Expand All @@ -45,16 +53,48 @@ jobs:
architecture: ${{ matrix.architecture }}

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

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

# Enable required repositories for the lyrical beta tarball; see:
# 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
apt-get update
apt-get install -y build-essential cmake tar bzip2 python3 python3-rosdep python3-colcon-common-extensions

- name: Install ROS2 from binary tarball (lyrical)
if: ${{ matrix.ros_distribution == 'lyrical' }}
run: |
curl -sL "${{ matrix.ros_tar_url }}" -o /tmp/ros2-lyrical.tar.bz2
mkdir -p /opt/ros/lyrical
tar xf /tmp/ros2-lyrical.tar.bz2 --strip-components=1 -C /opt/ros/lyrical
rm /tmp/ros2-lyrical.tar.bz2

rosdep init || true
rosdep update
rosdep install --rosdistro lyrical --from-paths /opt/ros/lyrical/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 != 'lyrical' }}
run: |
sudo apt install -y ros-${{ matrix.ros_distribution }}-test-msgs ros-${{ matrix.ros_distribution }}-mrpt-msgs

- name: Install Electron test dependencies
run: |
sudo apt install ros-${{ matrix.ros_distribution }}-test-msgs ros-${{ matrix.ros_distribution }}-mrpt-msgs
# Adjust dependencies based on Ubuntu version
LIBASOUND_PKG="libasound2"
if grep -q "24.04" /etc/os-release; then
if grep -Eq "24\.04|26\.04" /etc/os-release; then
LIBASOUND_PKG="libasound2t64"
fi
sudo apt install -y xvfb libgtk-3-0 libnss3 $LIBASOUND_PKG libgbm-dev
Expand Down
57 changes: 30 additions & 27 deletions .github/workflows/linux-x64-build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- humble
- jazzy
- kilted
- lyrical
- rolling
include:
# Humble Hawksbill (May 2022 - May 2027)
Expand All @@ -37,6 +38,14 @@ jobs:
# Kilted Kaiju (May 2025 - Dec 2026)
- docker_image: ubuntu:noble
ros_distribution: kilted
# Lyrical Luth (May 2026 - May 2031, beta)
# NOTE: the tarball URL below is a date-stamped beta snapshot and must
# be refreshed manually until upstream publishes a stable "latest"
# download (or until GA), at which point it can be replaced with the
# stable URL similar to the rolling nightly redirect.
- docker_image: ubuntu:26.04
ros_distribution: lyrical
ros_tar_url: "https://github.com/ros2/ros2/releases/download/release-lyrical-beta-20260430/ros2-lyrical-2026-04-30-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 +60,45 @@ 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. The same setup applies to both the
# rolling nightly tarball and the lyrical beta tarball; see the
# per-distro install docs for reference:
# rolling: https://docs.ros.org/en/rolling/Installation/Alternatives/Ubuntu-Install-Binary.html
# lyrical: 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 +115,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 +124,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
18 changes: 18 additions & 0 deletions .github/workflows/windows-build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ jobs:
ros_distribution:
- jazzy
- kilted
- lyrical
- rolling
include:
- ros_distribution: jazzy
Expand All @@ -26,6 +27,11 @@ jobs:
- ros_distribution: kilted
ros_zip_url: "https://github.com/ros2/ros2/releases/download/release-kilted-20250728/ros2-kilted-20250728-windows-release-amd64.zip"
run_tests: false
# Lyrical Luth (May 2026 - May 2031, beta)
# Reference: https://docs.ros.org/en/lyrical/Installation/Windows-Install-Binary.html
- ros_distribution: lyrical
ros_zip_url: "https://github.com/ros2/ros2/releases/download/release-lyrical-beta-20260430/ros2-lyrical-2026-04-30-windows-AMD64.zip"
run_tests: false
- ros_distribution: rolling
ros_zip_url: "https://github.com/ros2/ros2/releases/download/release-rolling-nightlies/ros2-rolling-nightly-windows-amd64.zip"
run_tests: false
Expand All @@ -52,9 +58,21 @@ jobs:
choco install 7zip -y
Invoke-WebRequest -Uri "${{ matrix.ros_zip_url }}" -OutFile ros2.zip
7z x ros2.zip -y -oC:\pixi_ws
if (!(Test-Path C:\pixi_ws\ros2-windows\setup.bat)) {
$setupScript = Get-ChildItem -Path C:\pixi_ws -Recurse -Filter setup.bat | Select-Object -First 1
if ($null -eq $setupScript) {
throw "Unable to find ROS 2 setup.bat after extracting ${{ matrix.ros_zip_url }}"
}
Move-Item -Path $setupScript.Directory.FullName -Destination C:\pixi_ws\ros2-windows
}
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/ros2/ros2/refs/heads/${{ matrix.ros_distribution }}/pixi.toml" -OutFile C:\pixi_ws\pixi.toml
Push-Location C:\pixi_ws
pixi install
if (Test-Path C:\pixi_ws\ros2-windows\preinstall_setup_windows.py) {
Push-Location C:\pixi_ws\ros2-windows
pixi run python preinstall_setup_windows.py
Pop-Location
}
Pop-Location

- name: Prebuild - Setup VS Dev Environment
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
16 changes: 8 additions & 8 deletions src/rcl_graph_bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ typedef rcl_ret_t (*rcl_get_info_by_topic_func_t)(
const char* topic_name, bool no_mangle,
rcl_topic_endpoint_info_array_t* info_array);

#if ROS_VERSION > 2505
#if ROS_VERSION >= 2605
typedef rcl_ret_t (*rcl_get_info_by_service_func_t)(
const rcl_node_t* node, rcutils_allocator_t* allocator,
const char* service_name, bool no_mangle,
rcl_service_endpoint_info_array_t* info_array);
#endif // ROS_VERSION > 2505
#endif // ROS_VERSION >= 2605

Napi::Value GetPublisherNamesAndTypesByNode(const Napi::CallbackInfo& info) {
Napi::Env env = info.Env();
Expand Down Expand Up @@ -264,7 +264,7 @@ Napi::Value GetSubscriptionsInfoByTopic(const Napi::CallbackInfo& info) {
"subscriptions", rcl_get_subscriptions_info_by_topic);
}

#if ROS_VERSION > 2505
#if ROS_VERSION >= 2605
Napi::Value GetInfoByService(
Napi::Env env, rcl_node_t* node, const char* service_name, bool no_mangle,
const char* type, rcl_get_info_by_service_func_t rcl_get_info_by_service) {
Expand Down Expand Up @@ -300,9 +300,9 @@ Napi::Value GetInfoByService(

return ConvertToJSServiceEndpointInfoList(env, &info_array);
}
#endif // ROS_VERSION > 2505
#endif // ROS_VERSION >= 2605

#if ROS_VERSION > 2505
#if ROS_VERSION >= 2605
Napi::Value GetClientsInfoByService(const Napi::CallbackInfo& info) {
RclHandle* node_handle = RclHandle::Unwrap(info[0].As<Napi::Object>());
rcl_node_t* node = reinterpret_cast<rcl_node_t*>(node_handle->ptr());
Expand All @@ -322,7 +322,7 @@ Napi::Value GetServersInfoByService(const Napi::CallbackInfo& info) {
return GetInfoByService(info.Env(), node, service_name.c_str(), no_mangle,
"servers", rcl_get_servers_info_by_service);
}
#endif // ROS_VERSION > 2505
#endif // ROS_VERSION >= 2605

Napi::Object InitGraphBindings(Napi::Env env, Napi::Object exports) {
exports.Set("getPublisherNamesAndTypesByNode",
Expand All @@ -341,12 +341,12 @@ Napi::Object InitGraphBindings(Napi::Env env, Napi::Object exports) {
Napi::Function::New(env, GetPublishersInfoByTopic));
exports.Set("getSubscriptionsInfoByTopic",
Napi::Function::New(env, GetSubscriptionsInfoByTopic));
#if ROS_VERSION > 2505
#if ROS_VERSION >= 2605
exports.Set("getClientsInfoByService",
Napi::Function::New(env, GetClientsInfoByService));
exports.Set("getServersInfoByService",
Napi::Function::New(env, GetServersInfoByService));
#endif // ROS_VERSION > 2505
#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
Loading
Loading