Skip to content

Add GitHub Actions CI with a headless rendering smoke test#880

Open
j-rivero wants to merge 1 commit into
vrx4from
jrivero/vrx4_ci_clean
Open

Add GitHub Actions CI with a headless rendering smoke test#880
j-rivero wants to merge 1 commit into
vrx4from
jrivero/vrx4_ci_clean

Conversation

@j-rivero

@j-rivero j-rivero commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

Add GitHub Actions CI with a headless rendering smoke test

Bootstraps continuous integration for vrx4. There was no CI before; this builds the workspace and verifies the simulation actually comes up and renders on every push and PR.

CI workflow (.github/workflows/ci.yml)

A single smoke job that runs on push, pull_request against vrx4, and workflow_dispatch:

  • Runs in the ros:lyrical-ros-base container.
  • Installs Gazebo Jetty via the ros-gz packages (ros-lyrical-ros-gz-sim, ros-lyrical-ros-gz-bridge) from the ros2-testing repo, then resolves the remaining workspace deps with rosdep.
  • Builds with colcon build --merge-install, and explicitly asserts that vrx_bringup was installed (colcon exits 0 even when it builds nothing).
  • Caches the Gazebo Fuel assets pulled by the world so reruns are fast and less network-dependent (cached under /github/home/.gz/fuel to match the container HOME).

Headless smoke test (tools/smoke_test.sh + tools/check_render.py)

Exercises the real production launch (vrx_bringup simulation.launch.xml, gazebo_gui:=false) under a virtual X server (Xvfb) with Mesa software GL (llvmpipe), so rendering is validated without a GPU. The flow:

  1. Launch the production sim headless.
  2. Wait for the sim to come up via rclpy /clock detection (check_render.py --wait-only).
  3. Spawn a test-only observation camera into the running world and bridge its image to ROS just for the test.
  4. Assert over a measurement window that /clock is advancing (physics is stepping, not paused) and the camera is publishing frames — reporting the achieved frame rate.

It isolates its ROS graph per run, fails fast on launch crashes, and dumps the launch log on any failure.

Test-only observation camera

The observation camera is deliberately not part of any shipped world. It lives in tools/observation_camera.sdf and is spawned into the running sim at test time via ros_gz_sim create (the world already loads the Sensors system, so it renders). The production world, launch file, and bridge config carry no CI scaffolding.

Verification

Green end-to-end in CI. The smoke step confirms the runtime-spawned camera renders under software GL:

/clock detected; simulation is up.
[smoke] spawning observation camera into world 'default'...
clock advancing: True (688000000 -> 4266000000 ns)
CAMERA FPS: 6.70 (67 frames on /observation_camera/image over 10.0s)
PASS: sim stepping and camera rendering.

Bootstrap GitHub Actions CI for vrx4. A single job builds the workspace in
the ros:lyrical-ros-base container with Gazebo Jetty (ros-gz from the
ros2-testing repo) and runs a headless smoke test under Xvfb + Mesa software
GL: it launches the real production launch, confirms /clock is advancing,
and measures the frame rate of an observation camera.

The observation camera is test-only and is not part of any shipped world.
smoke_test.sh waits for the sim to come up (rclpy /clock detection), spawns
the camera into the running world via ros_gz_sim create, and bridges its
image just for the duration of the test, so the production world, launch,
and bridge config carry no CI scaffolding.

Also caches the Gazebo Fuel assets for faster, less network-dependent
reruns, adds the checker's test deps to vrx_bringup, and updates the README
requirement from ROS 2 Rolling to ROS 2 Lyrical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Jose Luis Rivero <jrivero@honurobotics.com>
@j-rivero j-rivero force-pushed the jrivero/vrx4_ci_clean branch from db3640e to 00fde6e Compare June 2, 2026 18:14

@caguero caguero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code mostly feels generic. How do you feel about moving it to its own GitHub composite action? The VRX repo will stay cleaner and more projects can benefit from it.

Comment thread .github/workflows/ci.yml
with:
path: src/vrx

# open_water.sdf pulls the Portuguese Ledge tile from Gazebo Fuel on first

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd remove the concrete references to an sdf world or model, as this might change in the future.

Comment thread tools/check_render.py
@@ -0,0 +1,144 @@
#!/usr/bin/env python3

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you include a license header in this file?

Comment thread tools/smoke_test.sh
@@ -0,0 +1,121 @@
#!/usr/bin/env bash

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a license header?

Comment thread tools/smoke_test.sh
@@ -0,0 +1,121 @@
#!/usr/bin/env bash
# Headless smoke test for vrx4: GLX rendering under a virtual X server (Xvfb)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd remove the reference to the specific branch and Gazebo/ROS versions, in case we change it in the future.

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.

2 participants