From 759f77f58a0f158571a88a998730750623a8a653 Mon Sep 17 00:00:00 2001 From: Matt Spencer Date: Mon, 13 Jul 2026 09:42:48 +0000 Subject: [PATCH] Add devcontainer --- .devcontainer/cyclonedds.xml | 13 +++++ .devcontainer/jazzy/Dockerfile.jazzy | 76 +++++++++++++++++++++++++++ .devcontainer/jazzy/devcontainer.json | 42 +++++++++++++++ .devcontainer/post-create.sh | 43 +++++++++++++++ .devcontainer/refresh-rosdep-cache.sh | 38 ++++++++++++++ 5 files changed, 212 insertions(+) create mode 100644 .devcontainer/cyclonedds.xml create mode 100644 .devcontainer/jazzy/Dockerfile.jazzy create mode 100644 .devcontainer/jazzy/devcontainer.json create mode 100644 .devcontainer/post-create.sh create mode 100755 .devcontainer/refresh-rosdep-cache.sh diff --git a/.devcontainer/cyclonedds.xml b/.devcontainer/cyclonedds.xml new file mode 100644 index 0000000..655b393 --- /dev/null +++ b/.devcontainer/cyclonedds.xml @@ -0,0 +1,13 @@ + + + + + + auto + 1000 + + + \ No newline at end of file diff --git a/.devcontainer/jazzy/Dockerfile.jazzy b/.devcontainer/jazzy/Dockerfile.jazzy new file mode 100644 index 0000000..a8c5458 --- /dev/null +++ b/.devcontainer/jazzy/Dockerfile.jazzy @@ -0,0 +1,76 @@ +FROM osrf/ros:jazzy-desktop-full +ARG USERNAME=USERNAME +ARG USER_UID=1000 +ARG USER_GID=$USER_UID + +# Delete user if it exists in container (e.g Ubuntu Noble: ubuntu) +RUN if id -u $USER_UID ; then userdel `id -un $USER_UID` ; fi + +# Create the user +RUN groupadd --gid $USER_GID $USERNAME \ + && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \ + # + # [Optional] Add sudo support. Omit if you don't need to install software after connecting. + && apt-get update \ + && apt-get install -y --no-install-recommends sudo \ + && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME \ + && rm -rf /var/lib/apt/lists/* + +# Make sure the base image is completely up to date +RUN apt-get update \ + && apt-get upgrade -y \ + && rm -rf /var/lib/apt/lists/* + + +# C++ development tools +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + gdb \ + clang \ + clang-format \ + clang-tidy \ + libboost-all-dev \ + && rm -rf /var/lib/apt/lists/* + +# Python development tools +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + python3-pip \ + python3-dev \ + python3-argcomplete \ + python3-colcon-common-extensions \ + python3-colcon-mixin \ + python3-rosdep \ + python3-vcstool \ + && rm -rf /var/lib/apt/lists/* + +# Optional: pre-install apt dependencies resolved from rosdep on a previous run. +COPY .devcontainer/jazzy/ /tmp/devcontainer-jazzy/ + +RUN apt-get update \ + && if [ -f /tmp/devcontainer-jazzy/rosdep-apt-packages.txt ]; then \ + grep -Ev '^\s*($|#)' /tmp/devcontainer-jazzy/rosdep-apt-packages.txt | xargs -r apt-get install -y --no-install-recommends; \ + fi \ + && rm -rf /var/lib/apt/lists/* + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ros-${ROS_DISTRO}-rmw-cyclonedds-cpp \ + && rm -rf /var/lib/apt/lists/* + +# ROS 2 ament build tools +# RUN apt-get install -y \ +# ros-${ROS_DISTRO}-ament-cmake \ +# ros-${ROS_DISTRO}-ament-cmake-auto \ +# ros-${ROS_DISTRO}-ament-python \ +# ros-${ROS_DISTRO}-ament-lint-auto \ +# ros-${ROS_DISTRO}-ament-lint-common + +ENV SHELL=/bin/bash + +# [Optional] Set the default user. Omit if you want to keep the default as root. +USER $USERNAME +CMD ["/bin/bash"] diff --git a/.devcontainer/jazzy/devcontainer.json b/.devcontainer/jazzy/devcontainer.json new file mode 100644 index 0000000..2f9a680 --- /dev/null +++ b/.devcontainer/jazzy/devcontainer.json @@ -0,0 +1,42 @@ +{ + "name": "ROS 2 Jazzy", + "privileged": true, + "remoteUser": "${localEnv:USER}", + "build": { + "context": "../..", + "dockerfile": "Dockerfile.jazzy", + "args": { + "USERNAME": "${localEnv:USER}" + } + }, + "workspaceFolder": "/workspaces/agv_pro_ros2", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "anthropic.claude-code" + ] + } + }, + "containerEnv": { + "DISPLAY": ":0", + "ROS_AUTOMATIC_DISCOVERY_RANGE": "SUBNET", + "ROS_DOMAIN_ID": "0", + "RMW_IMPLEMENTATION": "rmw_cyclonedds_cpp", + "CYCLONEDDS_URI": "/workspaces/agv_pro_ros2/.devcontainer/cyclonedds.xml" + }, + "runArgs": [ + "--network=robot-sim", + "--pid=host", + "--ipc=host", + "-e", + "DISPLAY=${env:DISPLAY}" + ], + "mounts": [ + "source=/tmp/.X11-unix,target=/tmp/.X11-unix,type=bind,consistency=cached", + "source=/dev/dri,target=/dev/dri,type=bind,consistency=cached", + "source=/usr/local/share/ca-certificates,target=/usr/local/share/host-certificates,type=bind,consistency=cached", + "source=${localEnv:HOME}/.ssh,target=/home/${localEnv:USER}/.ssh,type=bind,consistency=cached" + ], + "postCreateCommand": "bash .devcontainer/post-create.sh" +} \ No newline at end of file diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh new file mode 100644 index 0000000..a5cfc09 --- /dev/null +++ b/.devcontainer/post-create.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ROSDEP_APT_LIST_FILE="${ROOT_DIR}/.devcontainer/${ROS_DISTRO}/rosdep-apt-packages.txt" + + +if [[ -f /usr/local/share/host-certificates/AMD_CA.crt ]]; then + sudo cp /usr/local/share/host-certificates/AMD_CA.crt /usr/local/share/ca-certificates/ + sudo update-ca-certificates +fi + +if [[ ! -f "${ROSDEP_APT_LIST_FILE}" ]]; then + echo "Missing ${ROSDEP_APT_LIST_FILE}." + echo "Run .devcontainer/refresh-rosdep-cache.sh and rebuild the devcontainer." + exit 1 +fi + +tmp_packages="$(mktemp)" +cleanup() { + rm -f "${tmp_packages}" +} +trap cleanup EXIT + +export DEBIAN_FRONTEND=noninteractive +sudo apt-get update -qq + +rosdep keys --from-paths src --ignore-src 2>/dev/null \ + | xargs -r rosdep resolve --rosdistro "${ROS_DISTRO}" --filter-for-installers=apt 2>/dev/null \ + | sed -e '/^#/d' -e '/^$/d' \ + | LC_ALL=C sort -u > "${tmp_packages}" + +if ! diff -u <(grep -Ev '^\s*($|#)' "${ROSDEP_APT_LIST_FILE}") "${tmp_packages}" >/dev/null; then + echo "rosdep-resolved apt packages differ from ${ROSDEP_APT_LIST_FILE}." + echo "Run .devcontainer/refresh-rosdep-cache.sh and rebuild the devcontainer." + exit 1 +fi + +echo "rosdep apt cache is current; no post-create package installation needed." + +if compgen -G "${ROOT_DIR}/src/robotnik/robotnik_simulation/debs/ros-jazzy-*.deb" > /dev/null; then + sudo apt-get install -y "${ROOT_DIR}"/src/robotnik/robotnik_simulation/debs/ros-jazzy-*.deb +fi \ No newline at end of file diff --git a/.devcontainer/refresh-rosdep-cache.sh b/.devcontainer/refresh-rosdep-cache.sh new file mode 100755 index 0000000..c92aee0 --- /dev/null +++ b/.devcontainer/refresh-rosdep-cache.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +ROS_DISTRO="${ROS_DISTRO:-jazzy}" +CACHE_DIR="${ROOT_DIR}/.devcontainer/${ROS_DISTRO}" +APT_LIST_FILE="${CACHE_DIR}/rosdep-apt-packages.txt" + +cd "${ROOT_DIR}" + +mkdir -p "${CACHE_DIR}" + +if [[ ! -f /etc/ros/rosdep/sources.list.d/20-default.list ]]; then + sudo rosdep init +fi + +rosdep update +export DEBIAN_FRONTEND=noninteractive +sudo apt-get update -qq + +tmp_packages="$(mktemp)" +cleanup() { + rm -f "${tmp_packages}" +} +trap cleanup EXIT + +rosdep keys --from-paths src --ignore-src 2>/dev/null \ + | xargs -r rosdep resolve --rosdistro "${ROS_DISTRO}" --filter-for-installers=apt 2>/dev/null \ + | sed -e '/^#/d' -e '/^$/d' \ + | LC_ALL=C sort -u > "${tmp_packages}" + +{ + echo "# Generated by .devcontainer/refresh-rosdep-cache.sh" + echo "# apt packages resolved from rosdep keys in src" + cat "${tmp_packages}" +} > "${APT_LIST_FILE}" + +echo "Updated ${APT_LIST_FILE}" \ No newline at end of file