Skip to content
Closed
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
36 changes: 36 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "vortex-pipeline-detection-devcontainer",
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The devcontainer expects the image 'vortex-pipeline-detection:latest' to already exist, but there's no indication in the configuration about how to build it first. Consider adding a 'build' section to devcontainer.json that references the Dockerfile, or add a comment documenting that users need to run 'docker/build.sh' before opening the devcontainer.

Suggested change
"name": "vortex-pipeline-detection-devcontainer",
"name": "vortex-pipeline-detection-devcontainer",
// Note: build this image by running 'docker/build.sh' before opening the devcontainer.

Copilot uses AI. Check for mistakes.
"image": "vortex-pipeline-detection:latest",
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "bash"
},
"extensions": [
"ms-azuretools.vscode-docker",
"ms-vscode.cpptools",
"ranch-hand-robotics.rde-pack",
"ms-python.vscode-pylance",
"twxs.cmake",
"ms-vsliveshare.vsliveshare",
"eamodio.gitlens",
"njpwerner.autodocstring",
"cschlosser.doxdocgen",
"xaver.clang-format",
"visualstudioexptteam.vscodeintellicode",
"ms-vscode-remote.remote-ssh",
"gxl.git-graph-3",
"ms-vscode-remote.remote-containers",
"gruntfuggly.todo-tree"
]
}
},
"remoteUser": "devuser",
"workspaceFolder": "/ros2_ws",
"workspaceMount": "source=${localWorkspaceFolder},target=/ros2_ws,type=bind,consistency=cached",
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The workspaceMount uses 'consistency=cached' which is a Docker Desktop for Mac optimization that is deprecated and has no effect on Linux. This option can be safely removed for cross-platform compatibility, as it's only relevant for macOS and is ignored elsewhere.

Suggested change
"workspaceMount": "source=${localWorkspaceFolder},target=/ros2_ws,type=bind,consistency=cached",
"workspaceMount": "source=${localWorkspaceFolder},target=/ros2_ws,type=bind",

Copilot uses AI. Check for mistakes.
"runArgs": [
"--privileged",
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The --privileged flag grants the container extensive access to the host system, which poses security risks. Unless there's a specific requirement for hardware access (e.g., GPU, camera devices), consider using more targeted capabilities with --cap-add or --device flags instead. If privileged access is necessary, this should be documented explaining why it's required.

Suggested change
"--privileged",

Copilot uses AI. Check for mistakes.
"--network=host",
"--ipc=host"
]
}
30 changes: 30 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Ignore build artifacts and large/runtime folders
build/
install/
log/
*.log

# Docker and local config
docker/
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The docker/ directory is included in .dockerignore, which means the Dockerfile itself will be excluded from the build context. While this doesn't affect the build (since the Dockerfile is specified with --file), it could cause confusion or issues if scripts or documentation reference files in the docker/ directory from within the container. Consider if this exclusion is intentional.

Suggested change
docker/

Copilot uses AI. Check for mistakes.
.dockerignore

# VCS
.git
.gitignore

# VS Code
.vscode/

# Python
__pycache__/
*.pyc
*.pyo
*.pyd
venv/
.env

# Misc
*.swp
*.swo
node_modules/
bags/
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
devel/
logs/
build/
install/
log/
bin/
lib/
msg_gen/
Expand Down
69 changes: 69 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# ----------------------------------------------------------------------------
# Base Image
# ----------------------------------------------------------------------------
ARG BASE_IMAGE=ros:humble
FROM ${BASE_IMAGE}

# ----------------------------------------------------------------------------
# Setup Arguments & Environment
# ----------------------------------------------------------------------------
USER root
SHELL ["/bin/bash", "-c"]
ARG DEBIAN_FRONTEND=noninteractive
ARG ROS_DISTRO=humble

# Define User Args
ARG USER_ID=1000
ARG GROUP_ID=1000
ARG USERNAME=devuser

ENV WORKSPACE=/ros2_ws
WORKDIR ${WORKSPACE}

# ----------------------------------------------------------------------------
# Create non-root user
# ----------------------------------------------------------------------------
RUN groupadd --gid ${GROUP_ID} ${USERNAME} || true && \
useradd --uid ${USER_ID} --gid ${GROUP_ID} -m -s /bin/bash ${USERNAME} || true && \
Comment on lines +26 to +27
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The useradd and groupadd commands use '|| true' to suppress errors if the user/group already exists, which is good. However, this could mask other types of failures. Consider checking if the user/group already exists first with 'getent' before attempting to create them, or at least log when the commands fail for debugging purposes.

Suggested change
RUN groupadd --gid ${GROUP_ID} ${USERNAME} || true && \
useradd --uid ${USER_ID} --gid ${GROUP_ID} -m -s /bin/bash ${USERNAME} || true && \
RUN if ! getent group "${GROUP_ID}" >/dev/null 2>&1 && \
! getent group "${USERNAME}" >/dev/null 2>&1; then \
groupadd --gid "${GROUP_ID}" "${USERNAME}"; \
fi && \
if ! id -u "${USERNAME}" >/dev/null 2>&1; then \
useradd --uid "${USER_ID}" --gid "${GROUP_ID}" -m -s /bin/bash "${USERNAME}"; \
fi && \

Copilot uses AI. Check for mistakes.
apt-get update && apt-get install -y sudo && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
apt-get clean && rm -rf /var/lib/apt/lists/*

Comment on lines +28 to +31
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

Granting devuser passwordless sudo with NOPASSWD:ALL removes privilege separation inside the container, allowing any process running as this user to escalate to root without authentication. If an attacker can execute code as devuser (for example via ROS nodes or helper scripts), they can trivially gain root in the container and, when combined with elevated Docker runtime flags, may be able to affect the host as well. Avoid broad NOPASSWD:ALL rules by running services as a non-root user without sudo, or by restricting sudo to a minimal, well-defined set of commands.

Suggested change
apt-get update && apt-get install -y sudo && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
apt-get clean && rm -rf /var/lib/apt/lists/*
apt-get update && apt-get clean && rm -rf /var/lib/apt/lists/*

Copilot uses AI. Check for mistakes.
# ----------------------------------------------------------------------------
# Install System Dependencies
# ----------------------------------------------------------------------------
RUN apt-get update && apt-get install -y \
git \
python3-vcstool \
python3-pip \
ros-${ROS_DISTRO}-cv-bridge \
ros-${ROS_DISTRO}-vision-msgs \
ros-${ROS_DISTRO}-pcl-conversions \
libopencv-dev \
libpcl-dev \
python3-colcon-common-extensions \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

Comment on lines +24 to +46
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The Dockerfile runs apt-get update and install in multiple separate RUN commands (lines 28 and 35). This creates additional layers and prevents Docker from optimizing the cache effectively. Consider combining these into a single RUN command or using a multi-stage build pattern to reduce the final image size and build time.

Suggested change
# Create non-root user
# ----------------------------------------------------------------------------
RUN groupadd --gid ${GROUP_ID} ${USERNAME} || true && \
useradd --uid ${USER_ID} --gid ${GROUP_ID} -m -s /bin/bash ${USERNAME} || true && \
apt-get update && apt-get install -y sudo && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# ----------------------------------------------------------------------------
# Install System Dependencies
# ----------------------------------------------------------------------------
RUN apt-get update && apt-get install -y \
git \
python3-vcstool \
python3-pip \
ros-${ROS_DISTRO}-cv-bridge \
ros-${ROS_DISTRO}-vision-msgs \
ros-${ROS_DISTRO}-pcl-conversions \
libopencv-dev \
libpcl-dev \
python3-colcon-common-extensions \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Create non-root user & install system dependencies
# ----------------------------------------------------------------------------
RUN groupadd --gid ${GROUP_ID} ${USERNAME} || true && \
useradd --uid ${USER_ID} --gid ${GROUP_ID} -m -s /bin/bash ${USERNAME} || true && \
apt-get update && apt-get install -y \
sudo \
git \
python3-vcstool \
python3-pip \
ros-${ROS_DISTRO}-cv-bridge \
ros-${ROS_DISTRO}-vision-msgs \
ros-${ROS_DISTRO}-pcl-conversions \
libopencv-dev \
libpcl-dev \
python3-colcon-common-extensions && \
echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# ----------------------------------------------------------------------------
# Install Python dependencies
# ----------------------------------------------------------------------------

Copilot uses AI. Check for mistakes.
RUN pip3 install cython
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The pip3 install command doesn't use the --no-cache-dir flag, which means pip will cache downloaded packages. This increases the image size unnecessarily. Consider adding --no-cache-dir to reduce the final image size.

Suggested change
RUN pip3 install cython
RUN pip3 install --no-cache-dir cython

Copilot uses AI. Check for mistakes.

# ----------------------------------------------------------------------------
# Copy Workspace Files
# ----------------------------------------------------------------------------
COPY . ${WORKSPACE}
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The Dockerfile copies the entire workspace with COPY . but doesn't provide any validation that required files or directories exist. If the build is run from an unexpected location or if required ROS package files are missing, the build will fail later during rosdep install with unclear error messages. Consider adding validation steps or documentation about build requirements.

Suggested change
COPY . ${WORKSPACE}
COPY . ${WORKSPACE}
# Validate that the build context is a ROS 2 workspace with a src directory
RUN if [ ! -d "${WORKSPACE}/src" ]; then \
echo "ERROR: Expected ROS 2 workspace source directory '\${WORKSPACE}/src' not found."; \
echo "Please run 'docker build' from the root of the ROS 2 workspace that contains a 'src' directory."; \
exit 1; \
fi

Copilot uses AI. Check for mistakes.
RUN chown -R ${USER_ID}:${GROUP_ID} ${WORKSPACE}

# ----------------------------------------------------------------------------
# Install ROS dependencies
# ----------------------------------------------------------------------------
RUN rosdep update && rosdep install --from-paths . --ignore-src -r -y

# ----------------------------------------------------------------------------
# User Configuration
# ----------------------------------------------------------------------------
RUN echo "if [ -f /ros2_ws/scripts/bashrc_extra.sh ]; then source /ros2_ws/scripts/bashrc_extra.sh; fi" >> /home/${USERNAME}/.bashrc

# Switch to the user
USER ${USERNAME}
WORKDIR /home/${USERNAME}
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The final WORKDIR is set to /home/devuser, but the workspace is at /ros2_ws. This means when a container starts, users will be in their home directory rather than the workspace. Consider setting WORKDIR to /ros2_ws or using the WORKSPACE environment variable to maintain consistency with the workspace location used throughout the rest of the Dockerfile.

Suggested change
WORKDIR /home/${USERNAME}
WORKDIR ${WORKSPACE}

Copilot uses AI. Check for mistakes.

CMD ["bash"]
58 changes: 58 additions & 0 deletions docker/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env bash
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The Docker setup is missing documentation. Users need guidance on how to build and run the Docker container, what the scripts do, prerequisites (e.g., Docker, buildx), and any specific requirements like hardware access that justify the privileged flag. Consider adding a Docker section to the README.md or creating a dedicated docker/README.md file.

Suggested change
#!/usr/bin/env bash
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# Docker Image Build Script
# ------------------------------------------------------------------------------
# This script builds the project Docker image using `docker buildx build`.
#
# It is intended as the primary entry point for building the development or
# runtime image for this repository.
#
# Prerequisites
# - Docker Engine installed and running
# - Docker Buildx available (Docker Desktop usually includes this by default,
# or you can enable it via `docker buildx create --use`).
# - Sufficient permissions to run Docker commands (e.g., being in the `docker`
# group on Linux or running with sudo if required).
#
# What this script does
# - Detects the host CPU architecture and maps it to a Docker platform string
# (e.g., `linux/amd64` or `linux/arm64`).
# - Builds the Docker image defined by `docker/Dockerfile` using buildx.
# - Passes the current user and group IDs into the build as build arguments,
# allowing the image to be configured with matching user permissions.
#
# Usage
# - From the repository root:
# ./docker/build.sh
#
# - From within the `docker/` directory:
# ./build.sh
#
# The script automatically determines the workspace root and uses it as the
# build context. No additional arguments are required for a standard build.
#
# Configuration
# - IMAGE: Name/tag for the resulting Docker image.
# - BASE_IMAGE: Base image used by the Dockerfile. You can override these by
# exporting the variables before running the script, for example:
# IMAGE=my-image:dev BASE_IMAGE=ros:humble ./docker/build.sh
#
# Notes
# - This script only performs a build. Running the resulting container (and any
# additional runtime requirements such as hardware access or `--privileged`)
# should be documented in the corresponding run script or project README.
# - If `docker buildx` is not yet configured on your machine, consult the
# Docker documentation for setting up Buildx.
# ------------------------------------------------------------------------------

Copilot uses AI. Check for mistakes.
set -euo pipefail

# ------------------------------------------------------------------------------
# Environment Variables
# ------------------------------------------------------------------------------
IMAGE="vortex-pipeline-detection:latest" # Docker image name/tag
BASE_IMAGE="ros:humble" # Base image for Docker builds
Comment on lines +7 to +8
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The script hardcodes the image name 'vortex-pipeline-detection:latest' without providing an easy way to override it via environment variables. Consider allowing IMAGE to be overridden by an environment variable (e.g., IMAGE="${IMAGE:-vortex-pipeline-detection:latest}") to make the script more flexible for CI/CD pipelines or different image tags.

Suggested change
IMAGE="vortex-pipeline-detection:latest" # Docker image name/tag
BASE_IMAGE="ros:humble" # Base image for Docker builds
IMAGE="${IMAGE:-vortex-pipeline-detection:latest}" # Docker image name/tag
BASE_IMAGE="ros:humble" # Base image for Docker builds

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The BASE_IMAGE is hardcoded to 'ros:humble' without providing an easy way to override it via environment variables. Consider allowing BASE_IMAGE to be overridden by an environment variable (e.g., BASE_IMAGE="${BASE_IMAGE:-ros:humble}") to make it easier to test with different ROS base images or versions.

Suggested change
BASE_IMAGE="ros:humble" # Base image for Docker builds
BASE_IMAGE="${BASE_IMAGE:-ros:humble}" # Base image for Docker builds (overridable via env)

Copilot uses AI. Check for mistakes.

# ------------------------------------------------------------------------------
# Platform Detection
# ------------------------------------------------------------------------------
ARCHITECTURE="$(uname -m)"
if [[ "$ARCHITECTURE" == "arm64" || "$ARCHITECTURE" == "aarch64" ]]; then
PLATFORM="linux/arm64"
elif [[ "$ARCHITECTURE" == "x86_64" ]]; then
PLATFORM="linux/amd64"
else
echo "Unsupported architecture: $ARCHITECTURE" >&2
exit 1
fi

# ------------------------------------------------------------------------------
# Locate Script and Workspace Root
# ------------------------------------------------------------------------------
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
WORKSPACE="$(realpath "$SCRIPT_DIR/..")"

# ------------------------------------------------------------------------------
# Build Start Info
# ------------------------------------------------------------------------------
echo "======================================================================"
echo " Building Docker Image"
echo " • PLATFORM: $PLATFORM"
echo " • BASE_IMAGE: $BASE_IMAGE"
echo " • IMAGE: $IMAGE"
echo " • BUILD CONTEXT: $WORKSPACE"
echo "======================================================================"
echo ""

# ------------------------------------------------------------------------------
# Build Docker Image with Buildx
# ------------------------------------------------------------------------------
docker buildx build \
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The script uses 'set -euo pipefail' which is good for error handling. However, when the docker buildx build command fails, the error message from Docker might not be immediately clear about what went wrong. Consider adding a trap to provide a more helpful error message if the build fails, especially since this is a new Docker setup.

Copilot uses AI. Check for mistakes.
--platform "$PLATFORM" \
--network=host \
--build-arg BASE_IMAGE="$BASE_IMAGE" \
--build-arg USER_ID="$(id -u)" \
--build-arg GROUP_ID="$(id -g)" \
--tag "$IMAGE" \
--file "$SCRIPT_DIR/Dockerfile" \
--load \
"$WORKSPACE"

echo ""
echo "======================================================================"
echo " Successfully built image '$IMAGE' for platform '$PLATFORM'"
echo "======================================================================"
32 changes: 32 additions & 0 deletions docker/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
set -euo pipefail

# ------------------------------------------------------------------------------
# Image Configuration
# ------------------------------------------------------------------------------
IMAGE="vortex-pipeline-detection:latest" # Default Docker image name/tag
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

The script hardcodes the image name 'vortex-pipeline-detection:latest' without providing an easy way to override it via environment variables. Consider allowing IMAGE to be overridden by an environment variable (e.g., IMAGE="${IMAGE:-vortex-pipeline-detection:latest}") to make the script more flexible for CI/CD pipelines or different image tags.

Suggested change
IMAGE="vortex-pipeline-detection:latest" # Default Docker image name/tag
IMAGE="${IMAGE:-vortex-pipeline-detection:latest}" # Default Docker image name/tag

Copilot uses AI. Check for mistakes.

# ------------------------------------------------------------------------------
# Locate Script Directory and Workspace Root
# ------------------------------------------------------------------------------
SCRIPT_DIR="$(dirname "$(realpath "$0")")"
WORKSPACE="$(realpath "$SCRIPT_DIR/..")"

echo "======================================================================"
echo " Running Container"
echo " • IMAGE: $IMAGE"
echo " • Volume mount: $WORKSPACE -> /ros2_ws"
echo "======================================================================"
echo ""

# ------------------------------------------------------------------------------
# Run Docker Container
# ------------------------------------------------------------------------------
docker run -it --rm \
--privileged \
--network host \
--ipc=host \
Comment on lines +26 to +28
Copy link

Copilot AI Jan 8, 2026

Choose a reason for hiding this comment

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

Running the container with --privileged, --network host, and --ipc=host effectively disables key Docker isolation boundaries, giving processes inside the container direct access to host devices, network stack, and IPC. If any code inside the container is compromised (e.g., via ROS nodes or build scripts), an attacker can leverage these flags to fully compromise the host. Tighten the container runtime configuration by removing --privileged, avoiding host networking/IPC unless absolutely required, and granting only the minimal capabilities/devices needed for this workload.

Suggested change
--privileged \
--network host \
--ipc=host \

Copilot uses AI. Check for mistakes.
-v "$WORKSPACE":/ros2_ws \
-w /ros2_ws \
"$IMAGE" \
/bin/bash
19 changes: 19 additions & 0 deletions scripts/bashrc_extra.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/bin/bash
# /ros2_ws/scripts/bashrc_extra.sh for ROS 2 container

# Print actions for clarity
if [ -f /opt/ros/humble/setup.bash ]; then
echo "[bashrc_extra] Sourcing ROS 2 underlay: /opt/ros/humble/setup.bash"
source /opt/ros/humble/setup.bash
else
echo "[bashrc_extra] ROS 2 underlay not found: /opt/ros/humble/setup.bash"
fi

if [ -f /ros2_ws/install/setup.bash ]; then
echo "[bashrc_extra] Sourcing workspace overlay: /ros2_ws/install/setup.bash"
source /ros2_ws/install/setup.bash
else
echo "[bashrc_extra] Workspace overlay not found: /ros2_ws/install/setup.bash"
fi

# Add your custom shell commands, aliases, or environment variables below