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
25 changes: 25 additions & 0 deletions .github/workflows/install-and-launch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
# - CI=true -> install.sh skips the host `xhost` check (no GUI on runners).
# - variant=arm runs on `ubuntu-24.04-arm` with `./install.sh --arm`; the built image
# is then verified to be linux/arm64 (linux/amd64 for the default variant).
# - Runner OS (22.04 or 24.04) is only the CI host. ROS Humble always runs inside
# Jammy-based images (`osrf/ros:humble-desktop` / `ros:humble-ros-base-jammy`).
# - `ubuntu-24.04-arm` is GitHub's hosted ARM64 runner label; there is no 22.04-arm
# standard runner. The container OS stays Jammy regardless.
# - The launch smoke test uses `./launch_lucy.sh --headless <command>`, which runs a
# single command inside the container (no control panel, no auto Gazebo/RViz).

Expand Down Expand Up @@ -51,6 +55,27 @@ jobs:
- name: Drop workspace .env if present
run: rm -f .env .env.local || true

# Docker Hub metadata/pull flakes (i/o timeout) fail `docker build` before
# any Dockerfile layer runs. Pre-pull with retries so the build reuses cache.
- name: Pre-pull base images (retry on Hub flakes)
run: |
pull_with_retry() {
image="$1"
for attempt in 1 2 3 4 5; do
if docker pull "$image"; then
return 0
fi
echo "docker pull ${image} failed (attempt ${attempt}/5), retrying..."
sleep $((attempt * 15))
done
return 1
}
if [ "${{ matrix.use_arm_install }}" = true ]; then
pull_with_retry ros:humble-ros-base-jammy
else
pull_with_retry osrf/ros:humble-desktop
fi

- name: Install (clone + Docker image + rosdep + colcon + yarn)
run: |
chmod +x install.sh launch_lucy.sh
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ __pycache__/
# Launcher remembered tick selection (persisted across container restarts)
.lucy_launcher_state.json

config/repos.json
# Local repo override (forks/branches; takes precedence over config/repos.json)
config/repos.json.local
66 changes: 58 additions & 8 deletions Dockerfile.humble
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lucy runtime image — ROS 2 Humble (Ubuntu 22.04 Jammy) with Gazebo Fortress,
# Lucy runtime image — ROS 2 Humble (Ubuntu 22.04 Jammy) with Gazebo Harmonic,
# ros2_control, RViz, rosbridge, GStreamer and Node 22 + Yarn for the control panel.
#
# Built and tagged as `lucy_ros2:humble` by docker/ensure_image.sh, which
Expand Down Expand Up @@ -53,22 +53,24 @@ RUN printf '%s\n' \
'Acquire::Retries "5";' \
> /etc/apt/apt.conf.d/99-lucy-mirror-resilience

# Gazebo Fortress (the `gz sim` binary used by ros_gz_sim launches).
# Gazebo Harmonic (the `gz sim` binary used by ros_gz_sim launches).
# Humble officially ships Fortress; Harmonic uses OSRF packages (ros-gzharmonic).
# https://gazebosim.org/docs/harmonic/ros_installation/
RUN apt-get update && apt-get install -y --no-install-recommends --fix-missing \
curl lsb-release gnupg \
&& curl -sSL https://packages.osrfoundation.org/gazebo.gpg -o /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] https://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" \
> /etc/apt/sources.list.d/gazebo-stable.list \
&& apt-get update && apt-get install -y --no-install-recommends --fix-missing ignition-fortress \

&& apt-get update && apt-get install -y --no-install-recommends --fix-missing gz-harmonic \
&& rm -rf /var/lib/apt/lists/*

# ROS sim/control stack + apt prerequisites for the rosdep run in install.sh
# (lucy_bringup / camera_ros / GStreamer / pytest).
# ros-humble-ros-gzharmonic* debs are amd64-only on packages.osrfoundation.org;
# arm64 builds ros_gz from source in the layer below (gazebosim/ros_gz#614).
RUN apt-get update && apt-get install -y --no-install-recommends \
ros-humble-ros-gz-sim \
ros-humble-ros-gz-bridge \
ros-humble-gz-ros2-control \
git wget \
ros-humble-simulation-interfaces \
ros-humble-ros2-control \
ros-humble-ros2-control-cmake \
ros-humble-ros2-controllers \
Expand All @@ -83,9 +85,57 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
gstreamer1.0-plugins-good \
libgstreamer-plugins-good1.0-0 \
tmux \
&& wget -q https://raw.githubusercontent.com/osrf/osrf-rosdep/master/gz/00-gazebo.list \
-O /etc/ros/rosdep/sources.list.d/00-gazebo.list \
&& rosdep update \
&& rm -rf /var/lib/apt/lists/*

# amd64: OSRF hosts ros-humble-ros-gzharmonic debs (not built for arm64).
RUN if [ "$(dpkg --print-architecture)" = "amd64" ]; then \
apt-get update && apt-get install -y --no-install-recommends \
ros-humble-ros-gzharmonic \
ros-humble-ros-gzharmonic-bridge \
&& rm -rf /var/lib/apt/lists/*; \
fi

# Humble+Harmonic: gz_ros2_control has no apt package on any arch; ros_gz has no
# arm64 debs — build from source (humble branch, GZ_VERSION=harmonic).
# https://control.ros.org/humble/doc/gz_ros2_control/doc/index.html
RUN bash -c 'set -e \
&& ARCH=$(dpkg --print-architecture) \
&& mkdir -p /opt/gz_ros2_control_ws/src \
&& if [ "$ARCH" = "arm64" ]; then \
git clone --depth 1 --branch humble https://github.com/gazebosim/ros_gz.git \
/opt/gz_ros2_control_ws/src/ros_gz; \
fi \
&& git clone --depth 1 --branch humble https://github.com/ros-controls/gz_ros2_control.git \
/opt/gz_ros2_control_ws/src/gz_ros2_control \
&& source /opt/ros/humble/setup.bash \
&& export GZ_VERSION=harmonic \
&& cd /opt/gz_ros2_control_ws \
&& if [ "$ARCH" = "arm64" ]; then \
apt-get update && apt-get install -y --no-install-recommends \
libgflags-dev \
ros-humble-actuator-msgs \
ros-humble-gps-msgs \
ros-humble-vision-msgs \
&& rm -rf /var/lib/apt/lists/* \
&& rosdep install -r --from-paths \
src/ros_gz/ros_gz_interfaces \
src/ros_gz/ros_gz_bridge \
src/ros_gz/ros_gz_sim \
src/gz_ros2_control \
--ignore-src --rosdistro humble -y \
&& colcon build --parallel-workers=1 \
--packages-select ros_gz_interfaces ros_gz_bridge ros_gz_sim gz_ros2_control; \
else \
rosdep install -r --from-paths src --ignore-src --rosdistro humble -y \
--skip-keys="ros_gz_bridge ros_gz_sim gz_ros2_control_demos" \
&& colcon build --packages-select gz_ros2_control; \
fi \
&& rm -rf /opt/gz_ros2_control_ws/build /opt/gz_ros2_control_ws/log \
/opt/gz_ros2_control_ws/src'

# Node 22 + Yarn — the lucy_control_panel Vite dev server runs inside this image.
RUN curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
Expand Down Expand Up @@ -116,4 +166,4 @@ RUN echo "alias exit=''" >> ~/.bashrc # Disable 'exit' to force launcher usage

WORKDIR /workspace
ENTRYPOINT ["/bin/bash"]
CMD ["-c", "source /opt/ros/humble/setup.bash && exec /bin/bash"]
CMD ["-c", "source /opt/ros/humble/setup.bash && source /opt/gz_ros2_control_ws/install/setup.bash && exec /bin/bash"]
22 changes: 22 additions & 0 deletions config/launcher_config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@
"readiness_timeout": 60,
"default_on": false
},
{
"id": "robot_inmoov",
"name": "Robot: InMoov",
Comment thread
charlesmadjeri marked this conversation as resolved.
"description": "(robot_package:=inmoov_urdf)",
"type": "modifier",
"dependencies": ["core"],
"conflicts": ["robot_thais"],
"command": "robot_package:=inmoov_urdf",
"requires_pkg": "inmoov_urdf",
"default_on": true
},
{
"id": "gazebo",
"name": "... with Simulator",
Expand All @@ -25,6 +36,17 @@
"readiness_timeout": 120,
"default_on": false
},
{
"id": "headless",
"name": "... headless",
"description": "(server-only, no GUI)",
"type": "modifier",
"dependencies": ["gazebo"],
"conflicts": [],
"command": "headless:=true",
"subitem": true,
"default_on": false
},
{
"id": "rviz",
"name": "... with Visualizer",
Expand Down
12 changes: 6 additions & 6 deletions config/repos.json
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
{
"repos": [
{
"name": "thais_urdf",
"branch": "dev",
"url_https": "https://github.com/Sentience-Robotics/thais_urdf.git",
"url_ssh": "git@github.com:Sentience-Robotics/thais_urdf.git"
"name": "inmoov_urdf",
"branch": "master",
"url_https": "https://github.com/Sentience-Robotics/inmoov_urdf.git",
"url_ssh": "git@github.com:Sentience-Robotics/inmoov_urdf.git"
},
{
"name": "lucy_ros_packages",
"branch": "dev",
"branch": "master",
"url_https": "https://github.com/Sentience-Robotics/lucy_ros_packages.git",
"url_ssh": "git@github.com:Sentience-Robotics/lucy_ros_packages.git"
},
{
"name": "lucy_control_panel",
"branch": "dev",
"branch": "master",
"url_https": "https://github.com/Sentience-Robotics/lucy_control_panel.git",
"url_ssh": "git@github.com:Sentience-Robotics/lucy_control_panel.git"
}
Expand Down
21 changes: 21 additions & 0 deletions docs/developer_lucy_packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,27 @@ Docker Desktop on Apple Silicon defaults to `linux/amd64` ROS images and runs th

`config/repos.json` carries both `url_https` (default) and `url_ssh` for each repo. To clone over SSH, copy `.env.example` to `.env` and set `DEV=true` before running `install.sh`. SSH keys must be configured for the relevant host.

### Local repo overrides (`config/repos.json.local`)

To point a repo at your own fork or a feature branch without editing the tracked `config/repos.json`, create **`config/repos.json.local`**. When present it is used instead of `repos.json` by both `install.sh` and the launcher (`windows/Lucy.py`), and it is gitignored so overrides are never committed.

Use the same structure as `repos.json` — list only the repos you want to override (or all of them). Each entry needs `name` (the folder under `src/`), `branch`, and both `url_https` and `url_ssh` (Developer Mode selects SSH, otherwise HTTPS):

```json
{
"repos": [
{
"name": "inmoov_urdf",
"branch": "my-feature-branch",
"url_https": "https://github.com/your-user/inmoov_urdf.git",
"url_ssh": "git@github.com:your-user/inmoov_urdf.git"
}
]
}
```

Delete the file to fall back to the tracked `repos.json`.

## `launch_lucy.sh`

Builds the Docker image if needed, mounts the workspace at `/workspace`, sources the built ROS overlay, then:
Expand Down
9 changes: 9 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
# combine with any other flag, e.g. --arm --build-only
#
# Optional .env (copy from .env.example): DEV=true selects `url_ssh` in repos.json (default: `url_https`).
# Optional config/repos.json.local (gitignored): overrides config/repos.json to point repos at forks/branches.

set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
Expand All @@ -32,7 +33,14 @@ fi
IMAGE_NAME="lucy_ros2:humble"
DOCKERFILE_PATH="$SCRIPT_DIR/Dockerfile.humble"
WORKSPACE="/workspace"
# config/repos.json.local (gitignored) overrides the tracked config/repos.json,
# so contributors can point repos at their own forks/branches without touching
# the committed file. Falls back to repos.json when no local override exists.
CONFIG_FILE="${SCRIPT_DIR}/config/repos.json"
if [ -f "${SCRIPT_DIR}/config/repos.json.local" ]; then
CONFIG_FILE="${SCRIPT_DIR}/config/repos.json.local"
echo "install.sh: using local repo override config/repos.json.local"
fi

# shellcheck disable=SC1091
source "$SCRIPT_DIR/docker/ensure_image.sh"
Expand Down Expand Up @@ -146,6 +154,7 @@ docker_workspace_install() {
local inner_cmd
read -r -d '' inner_cmd <<'EOS' || true
source /opt/ros/humble/setup.bash \
&& [ -f /opt/gz_ros2_control_ws/install/setup.bash ] && source /opt/gz_ros2_control_ws/install/setup.bash \
&& cd /workspace \
&& rosdep install --from-paths src --ignore-src -r -y --skip-keys="audio_common" \
&& rm -rf build/camera_ros install/camera_ros \
Expand Down
3 changes: 2 additions & 1 deletion launch_lucy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ DOCKER_PORT_ARGS=(
# Container scripts
# ----------------------------------------------------------------------------

SETUP="source /opt/ros/humble/setup.bash"
SETUP="source /opt/ros/humble/setup.bash && [ -f /opt/gz_ros2_control_ws/install/setup.bash ] && source /opt/gz_ros2_control_ws/install/setup.bash"
SOURCE_WORKSPACE="cd $WORKSPACE && source install/setup.bash"
LAUNCH_GAZEBO_RVIZ_BRIDGE_CP="ros2 launch lucy_bringup lucy.launch.py gazebo:=true rviz:=true"
LAUNCH_RVIZ_BRIDGE_CP="ros2 launch lucy_bringup lucy.launch.py rviz:=true"
Expand All @@ -203,6 +203,7 @@ LAUNCH_RVIZ_BRIDGE_CP="ros2 launch lucy_bringup lucy.launch.py rviz:=true"
read -r -d '' CONTAINER_PREAMBLE <<'EOS' || true
set -e
source /opt/ros/humble/setup.bash
[ -f /opt/gz_ros2_control_ws/install/setup.bash ] && source /opt/gz_ros2_control_ws/install/setup.bash
cd /workspace
if [[ ! -f install/setup.bash ]]; then
echo "Workspace not built. Run Install/Update via Lucy.py" >&2
Expand Down
Loading
Loading