Skip to content
Open
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
3 changes: 2 additions & 1 deletion .devcontainer/prepare-local-features.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
local_features_dir="${repo_root}/.devcontainer/local-features"

mkdir -p "${local_features_dir}"
rm -rf "${local_features_dir}/linux-x11-forwarding" "${local_features_dir}/x11" "${local_features_dir}/wayland"
rm -rf "${local_features_dir}/linux-x11-forwarding" "${local_features_dir}/ros2" "${local_features_dir}/x11" "${local_features_dir}/wayland"
cp -R "${repo_root}/features/src/linux-x11-forwarding" "${local_features_dir}/linux-x11-forwarding"
cp -R "${repo_root}/features/src/ros2" "${local_features_dir}/ros2"

echo "Prepared local smoke-test features in ${local_features_dir}"
8 changes: 8 additions & 0 deletions .devcontainer/ros2-feature-smoke/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "ros2-feature-smoke",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
"features": {
"../local-features/ros2": {}
},
"postCreateCommand": "bash -lc 'ros2 pkg prefix rviz2 >/dev/null && command -v colcon >/dev/null && command -v rosdep >/dev/null && command -v vcs >/dev/null'"
}
65 changes: 65 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,71 @@
],
"problemMatcher": []
},
{
"label": "Prepare Local Features",
"type": "process",
"command": "bash",
"args": [
".devcontainer/prepare-local-features.sh"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Local Smoke Test ROS 2 Feature",
"dependsOrder": "sequence",
"dependsOn": [
"Prepare Local Features"
],
"type": "process",
"command": "devcontainer",
"args": [
"up",
"--workspace-folder",
"${workspaceFolder}",
"--config",
"${workspaceFolder}/.devcontainer/ros2-feature-smoke/devcontainer.json"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Test ROS 2 Feature",
"type": "process",
"command": "devcontainer",
"args": [
"features",
"test",
"--features",
"ros2",
"--project-folder",
"./features"
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": []
},
{
"label": "Test All Features",
"type": "process",
"command": "bash",
"args": [
"features/test/test_all.sh"
],
"options": {
"cwd": "${workspaceFolder}"
},
"group": {
"kind": "test",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "Generate Feature Docs",
"type": "process",
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ Devcontainer templates repository focused on robotics and simulation.

- `.devcontainer/gz-smoke/devcontainer.json`
- `.devcontainer/linux-x11-forwarding-smoke/devcontainer.json`
- `.devcontainer/ros2-feature-smoke/devcontainer.json`

Before using the samples locally, run
`bash .devcontainer/prepare-local-features.sh` so `devcontainer up` can resolve
the unpublished Feature.
the unpublished Features.

On classic Xorg hosts where the authority cookie lives outside
`XDG_RUNTIME_DIR`, add a bind mount to `/tmp/devcontainer-xauthority-host`.
Expand Down
32 changes: 32 additions & 0 deletions features/src/ros2/NOTES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Supported Ubuntu releases

ROS 2 deb packages target a specific Ubuntu release. Choose a compatible base
image for the selected `distro`. The default `auto` setting selects:

| ROS 2 distribution | Ubuntu release |
|---|---|
| Humble | 22.04 (Jammy) |
| Jazzy | 24.04 (Noble) |
| Kilted | 24.04 (Noble) |
| Lyrical | 26.04 (Resolute) |

The installation stops with an explanatory error when the selected ROS 2 and
Ubuntu releases are incompatible. On Ubuntu 24.04, `auto` selects Jazzy because
it is the long-term ROS 2 release; select Kilted explicitly if desired.

The `package` option defaults to the GUI-oriented `desktop` installation. Select
`ros-base` for a smaller installation. In both cases, the Feature installs
`ros-dev-tools`, including common ROS development commands such as `colcon`,
`rosdep`, and `vcs`.

## Shell and workspace setup

The ROS installation is sourced automatically for the existing container user.
If the configured `workspace` contains `install/setup.sh`, that overlay is
sourced afterward. The Feature does not create or replace the container user.

ROS 2 packages installed from debs use Ubuntu's system Python. Conda or a
separately installed Python interpreter may be incompatible with those packages.

The Feature's ROS 2-to-Ubuntu compatibility data is stored in
`distributions.json` and packaged with the Feature.
58 changes: 58 additions & 0 deletions features/src/ros2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@

# ROS 2 (ros2)

Install ROS 2 and the tools needed to develop ROS packages.

## Example Usage

```json
"features": {
"ghcr.io/althack/devcontainers/ros2:0": {}
}
```

## Options

| Options Id | Description | Type | Default Value |
|-----|-----|-----|-----|
| distro | ROS 2 distribution to install. By default, select the recommended distribution for the container's Ubuntu release. | string | auto |
| package | ROS 2 metapackage to install. | string | desktop |
| workspace | Workspace whose install/setup.sh file should be sourced when present. | string | ${containerWorkspaceFolder} |

## Supported Ubuntu releases

ROS 2 deb packages target a specific Ubuntu release. Choose a compatible base
image for the selected `distro`. The default `auto` setting selects:

| ROS 2 distribution | Ubuntu release |
|---|---|
| Humble | 22.04 (Jammy) |
| Jazzy | 24.04 (Noble) |
| Kilted | 24.04 (Noble) |
| Lyrical | 26.04 (Resolute) |

The installation stops with an explanatory error when the selected ROS 2 and
Ubuntu releases are incompatible. On Ubuntu 24.04, `auto` selects Jazzy because
it is the long-term ROS 2 release; select Kilted explicitly if desired.

The `package` option defaults to the GUI-oriented `desktop` installation. Select
`ros-base` for a smaller installation. In both cases, the Feature installs
`ros-dev-tools`, including common ROS development commands such as `colcon`,
`rosdep`, and `vcs`.

## Shell and workspace setup

The ROS installation is sourced automatically for the existing container user.
If the configured `workspace` contains `install/setup.sh`, that overlay is
sourced afterward. The Feature does not create or replace the container user.

ROS 2 packages installed from debs use Ubuntu's system Python. Conda or a
separately installed Python interpreter may be incompatible with those packages.

The Feature's ROS 2-to-Ubuntu compatibility data is stored in
`distributions.json` and packaged with the Feature.


---

_Note: This file was auto-generated from the [devcontainer-feature.json](https://github.com/althack/devcontainers/blob/main/features/src/ros2/devcontainer-feature.json). Add additional notes to a `NOTES.md`._
39 changes: 39 additions & 0 deletions features/src/ros2/devcontainer-feature.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"id": "ros2",
"version": "0.0.0",
"name": "ROS 2",
"description": "Install ROS 2 and the tools needed to develop ROS packages.",
"documentationURL": "https://github.com/althack/devcontainers/tree/main/features/src/ros2",
"licenseURL": "https://github.com/althack/devcontainers/blob/main/LICENSE",
"options": {
"distro": {
"type": "string",
"proposals": [
"auto",
"humble",
"jazzy",
"kilted",
"lyrical"
],
"default": "auto",
"description": "ROS 2 distribution to install. By default, select the recommended distribution for the container's Ubuntu release."
},
"package": {
"type": "string",
"proposals": [
"ros-base",
"desktop"
],
"default": "desktop",
"description": "ROS 2 metapackage to install."
},
"workspace": {
"type": "string",
"default": "${containerWorkspaceFolder}",
"description": "Workspace whose install/setup.sh file should be sourced when present."
}
},
"installsAfter": [
"ghcr.io/devcontainers/features/common-utils"
]
}
26 changes: 26 additions & 0 deletions features/src/ros2/distributions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"ubuntuReleases": {
"jammy": {
"version": "22.04",
"defaultDistro": "humble",
"supportedDistros": [
"humble"
]
},
"noble": {
"version": "24.04",
"defaultDistro": "jazzy",
"supportedDistros": [
"jazzy",
"kilted"
]
},
"resolute": {
"version": "26.04",
"defaultDistro": "lyrical",
"supportedDistros": [
"lyrical"
]
}
}
}
128 changes: 128 additions & 0 deletions features/src/ros2/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#!/usr/bin/env bash

set -euo pipefail

ros_distro="${DISTRO:-auto}"
ros_package="${PACKAGE:-desktop}"
ros_workspace="${WORKSPACE:-${_REMOTE_USER_HOME:-/workspaces}/ros2_ws}"
feature_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
distribution_config="${feature_dir}/distributions.json"

if [[ "$(id -u)" -ne 0 ]]; then
echo "The ROS 2 Feature must run as root." >&2
exit 1
fi

if [[ ! -r /etc/os-release ]]; then
echo "Unable to determine the base operating system." >&2
exit 1
fi

# shellcheck disable=SC1091
source /etc/os-release

if [[ "${ID:-}" != "ubuntu" ]]; then
echo "ROS 2 deb packages are supported only on Ubuntu; found ${ID:-unknown}." >&2
exit 1
fi

case "${ros_package}" in
ros-base | desktop)
;;
*)
echo "Unsupported ROS 2 package: ${ros_package}." >&2
exit 1
;;
esac

export DEBIAN_FRONTEND=noninteractive

apt-get update
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
jq \
locales \
software-properties-common

ubuntu_codename="${UBUNTU_CODENAME:-${VERSION_CODENAME:-}}"
ubuntu_release="$(jq -c --arg codename "${ubuntu_codename}" '.ubuntuReleases[$codename] // empty' "${distribution_config}")"

if [[ -z "${ubuntu_release}" ]]; then
echo "Ubuntu ${VERSION_ID:-unknown} (${ubuntu_codename:-unknown}) is not supported by this ROS 2 Feature." >&2
exit 1
fi

ubuntu_version="$(jq -r '.version' <<<"${ubuntu_release}")"

if [[ "${ros_distro}" == "auto" ]]; then
ros_distro="$(jq -r '.defaultDistro' <<<"${ubuntu_release}")"
echo "Selected ROS 2 ${ros_distro} for Ubuntu ${VERSION_ID:-unknown} (${ubuntu_codename})."
fi

if ! jq -e --arg distro "${ros_distro}" '.supportedDistros | index($distro) != null' <<<"${ubuntu_release}" >/dev/null; then
supported_distros="$(jq -r '.supportedDistros | join(", ")' <<<"${ubuntu_release}")"
echo "ROS 2 ${ros_distro} is not supported on Ubuntu ${ubuntu_version} (${ubuntu_codename}); select one of: ${supported_distros}." >&2
exit 1
fi

locale-gen en_US en_US.UTF-8
update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
add-apt-repository -y universe

ros_apt_source_release_url="$(
curl --fail --silent --show-error --location \
--output /dev/null \
--write-out '%{url_effective}' \
https://github.com/ros-infrastructure/ros-apt-source/releases/latest
)"
ros_apt_source_version="${ros_apt_source_release_url##*/}"

if [[ ! "${ros_apt_source_version}" =~ ^[0-9]+(\.[0-9]+)+$ ]]; then
echo "Unable to determine the latest ros2-apt-source release." >&2
exit 1
fi

ros_apt_source_package="/tmp/ros2-apt-source.deb"
curl --fail --silent --show-error --location \
"https://github.com/ros-infrastructure/ros-apt-source/releases/download/${ros_apt_source_version}/ros2-apt-source_${ros_apt_source_version}.${ubuntu_codename}_all.deb" \
--output "${ros_apt_source_package}"
dpkg -i "${ros_apt_source_package}"
rm -f "${ros_apt_source_package}"

apt-get update
apt-get install -y --no-install-recommends \
"ros-${ros_distro}-${ros_package}" \
python3-argcomplete \
ros-dev-tools

if [[ ! -e /etc/ros/rosdep/sources.list.d/20-default.list ]]; then
rosdep init
fi

cat > /etc/profile.d/ros2.sh <<EOF
if [ -f "/opt/ros/${ros_distro}/setup.sh" ]; then
. "/opt/ros/${ros_distro}/setup.sh"
fi

if [ -f "${ros_workspace}/install/setup.sh" ]; then
. "${ros_workspace}/install/setup.sh"
fi
EOF
chmod 0644 /etc/profile.d/ros2.sh

for user_home in "${_REMOTE_USER_HOME:-}" "${_CONTAINER_USER_HOME:-}"; do
if [[ -z "${user_home}" || ! -d "${user_home}" ]]; then
continue
fi

shell_init="${user_home}/.bashrc"
touch "${shell_init}"
if ! grep -Fq '. /etc/profile.d/ros2.sh' "${shell_init}"; then
printf '\n%s\n' '[ -f /etc/profile.d/ros2.sh ] && . /etc/profile.d/ros2.sh' >> "${shell_init}"
fi
done

rm -rf /var/lib/apt/lists/*

echo "Installed ROS 2 ${ros_distro} (${ros_package}) with ROS development tools."
Loading