diff --git a/CHANGELOG b/CHANGELOG index 5918f7b..7618708 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.2.0] - 2025-05-09 + +### Added +- Added new `dockerfile_templates/python.txt` template where all python related packages are defined. Now python is istalled by default +- Added new oh-my-zsh plugins: zsh-autosuggestions, zsh-completions and zsh-syntax-highlighting +- Added `git-lfs` to `dockerfile_templates/install_common_packages.txt` + +### Fixed +- Fixed `zsh` autocompletion issue in ROS2 + +### Changed +- Introduced `dockerfile_templates/ros2_extra.txt` to explicitly define extra ROS packages and reduced scope of ros2.txt + +### Removed +- Removed python installation from `dockerfile_templates/conan.txt` as python is installed by default now +- Removed any pip upgrades from template files as pip is being upgraded by default at `dockerfile_templates/python.txt` + ## [3.1.1] - 2025-03-21 ### Fixed diff --git a/pyproject.toml b/pyproject.toml index cfb53f1..d372cc1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "turludock" -version = "3.1.1" +version = "3.2.0" description = "Builds ROS docker images that support GUI with either X11 or Wayland." authors = [ "Athanasios ", diff --git a/turludock/assets/dockerfile_templates/conan.txt b/turludock/assets/dockerfile_templates/conan.txt index e528682..9368446 100644 --- a/turludock/assets/dockerfile_templates/conan.txt +++ b/turludock/assets/dockerfile_templates/conan.txt @@ -1,7 +1,4 @@ # Install conan -RUN apt-get update && apt-get install -y python3-pip && \ - apt-get clean && rm -rf /var/lib/apt/lists/* - -RUN pip install --no-cache-dir --break-system-packages --upgrade pip || pip install --no-cache-dir --upgrade pip && \ - pip install --no-cache-dir --break-system-packages --ignore-installed PyYAML && \ +RUN pip install --no-cache-dir --break-system-packages --ignore-installed PyYAML && \ + pip install --no-cache-dir --break-system-packages --ignore-installed distro && \ pip install --no-cache-dir --break-system-packages conan \ No newline at end of file diff --git a/turludock/assets/dockerfile_templates/cpplint.txt b/turludock/assets/dockerfile_templates/cpplint.txt index f3a1020..a10ebc8 100644 --- a/turludock/assets/dockerfile_templates/cpplint.txt +++ b/turludock/assets/dockerfile_templates/cpplint.txt @@ -1,6 +1,2 @@ # Install cpplint -RUN apt-get update && apt-get install -y python3-pip && \ - apt-get clean && rm -rf /var/lib/apt/lists/* - -RUN pip install --user --no-cache-dir --break-system-packages --upgrade pip && \ - pip install --user --no-cache-dir --break-system-packages cpplint \ No newline at end of file +RUN pip install --user --no-cache-dir --break-system-packages cpplint \ No newline at end of file diff --git a/turludock/assets/dockerfile_templates/install_common_packages.txt b/turludock/assets/dockerfile_templates/install_common_packages.txt index fbf134c..dd4aa70 100644 --- a/turludock/assets/dockerfile_templates/install_common_packages.txt +++ b/turludock/assets/dockerfile_templates/install_common_packages.txt @@ -4,6 +4,7 @@ sudo \ locales \ lsb-release \ git \ +git-lfs \ subversion \ nano \ vim \ diff --git a/turludock/assets/dockerfile_templates/oh_my_zsh.txt b/turludock/assets/dockerfile_templates/oh_my_zsh.txt index f201750..40f0e28 100644 --- a/turludock/assets/dockerfile_templates/oh_my_zsh.txt +++ b/turludock/assets/dockerfile_templates/oh_my_zsh.txt @@ -5,5 +5,9 @@ RUN apt-get update && apt-get install -y zsh && apt-get clean && rm -rf /var/lib git clone https://github.com/sindresorhus/pure /root/.oh-my-zsh/custom/pure && \ ln -s /root/.oh-my-zsh/custom/pure/pure.zsh-theme /root/.oh-my-zsh/custom/ && \ ln -s /root/.oh-my-zsh/custom/pure/async.zsh /root/.oh-my-zsh/custom/ && \ + git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions && \ + git clone https://github.com/zsh-users/zsh-completions.git ${ZSH_CUSTOM:-${ZSH:-~/.oh-my-zsh}/custom}/plugins/zsh-completions && \ + git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting && \ sed -i -e 's/robbyrussell/refined/g' /root/.zshrc && \ - sed -i '/plugins=(/c\plugins=(git pyenv)' /root/.zshrc \ No newline at end of file + sed -i '/plugins=(/c\plugins=(git pyenv zsh-autosuggestions zsh-syntax-highlighting)' /root/.zshrc && \ + sed -i '/source \$ZSH\/oh-my-zsh\.sh/i # needed for zsh-completions\nfpath+=${ZSH_CUSTOM:-${ZSH:-~/.oh-my-zsh}/custom}/plugins/zsh-completions/src\nautoload -U compinit \&\& compinit\n' ~/.zshrc \ No newline at end of file diff --git a/turludock/assets/dockerfile_templates/python.txt b/turludock/assets/dockerfile_templates/python.txt new file mode 100644 index 0000000..e532257 --- /dev/null +++ b/turludock/assets/dockerfile_templates/python.txt @@ -0,0 +1,5 @@ +# Install Python and Python related packages +RUN apt-get update && apt-get install -y python3 python3-dev python3-venv python3-pip && \ + apt-get clean && rm -rf /var/lib/apt/lists/* +# Upgrade pip - but not all pip versions support "--break-system-packages" +RUN python3 -m pip install --upgrade pip --break-system-packages --ignore-installed || python3 -m pip install --upgrade pip \ No newline at end of file diff --git a/turludock/assets/dockerfile_templates/ros2.txt b/turludock/assets/dockerfile_templates/ros2.txt index 3a8a36d..a2a3ab3 100644 --- a/turludock/assets/dockerfile_templates/ros2.txt +++ b/turludock/assets/dockerfile_templates/ros2.txt @@ -5,10 +5,8 @@ RUN add-apt-repository -y universe && \ RUN apt-get update && apt-get install -y \ ros-$ros_version_short-desktop \ - ros-dev-tools \ - ros-$ros_version_short-perception \ - libpcl-dev && \ + ros-dev-tools && \ apt-get clean && rm -rf /var/lib/apt/lists/* RUN echo "source /opt/ros/$ros_version_short/setup.bash" >> /root/.bashrc && \ - echo "source /opt/ros/$ros_version_short/setup.zsh" >> /root/.zshrc \ No newline at end of file + echo "source /opt/ros/$ros_version_short/setup.zsh" >> /root/.zshrc diff --git a/turludock/assets/dockerfile_templates/ros2_autocomplete_fix.txt b/turludock/assets/dockerfile_templates/ros2_autocomplete_fix.txt new file mode 100755 index 0000000..646f60e --- /dev/null +++ b/turludock/assets/dockerfile_templates/ros2_autocomplete_fix.txt @@ -0,0 +1,4 @@ +# Fix for argcomplete for ros2 & colcon (https://github.com/ros2/ros2cli/issues/534#issuecomment-957516107) +RUN echo "\n# Fix for argcomplete for ros2 & colcon (https://github.com/ros2/ros2cli/issues/534#issuecomment-957516107)" >> /root/.zshrc && \ + echo 'eval "$$(/usr/bin/register-$python_argcomplete ros2)"' >> /root/.zshrc && \ + echo 'eval "$$(/usr/bin/register-$python_argcomplete colcon)"' >> /root/.zshrc \ No newline at end of file diff --git a/turludock/assets/dockerfile_templates/ros2_extra.txt b/turludock/assets/dockerfile_templates/ros2_extra.txt new file mode 100755 index 0000000..26c8dab --- /dev/null +++ b/turludock/assets/dockerfile_templates/ros2_extra.txt @@ -0,0 +1,21 @@ +# Extra ROS2 packages +RUN apt-get update && apt-get install -y \ + ros-$ros_version_short-rclpy-message-converter \ + ros-$ros_version_short-diagnostic-updater \ + ros-$ros_version_short-joint-state-publisher \ + ros-$ros_version_short-rosbag2-storage-mcap \ + ros-$ros_version_short-perception \ + ros-$ros_version_short-cv-bridge \ + ros-$ros_version_short-pcl-ros \ + ros-$ros_version_short-pcl-conversions \ + ros-$ros_version_short-point-cloud-transport \ + ros-$ros_version_short-visualization-msgs \ + ros-$ros_version_short-sensor-msgs \ + ros-$ros_version_short-geometry-msgs \ + ros-$ros_version_short-mavros-msgs \ + ros-$ros_version_short-nmea-msgs \ + ros-$ros_version_short-geographic-msgs \ + ros-$ros_version_short-vision-msgs \ + ros-$ros_version_short-v4l2-camera \ + libpcl-dev && \ + apt-get clean && rm -rf /var/lib/apt/lists/* \ No newline at end of file diff --git a/turludock/generate_dockerfile.py b/turludock/generate_dockerfile.py index 1806b88..fa77f96 100644 --- a/turludock/generate_dockerfile.py +++ b/turludock/generate_dockerfile.py @@ -14,6 +14,7 @@ generate_meld, generate_mesa, generate_ohmyzsh, + generate_python, generate_terminator, generate_vscode, ) @@ -31,6 +32,8 @@ generate_header_info, generate_llvm, generate_ros, + generate_ros_autocomplete_fix, + generate_ros_extra, generate_tmux, ) from turludock.helper_functions import ( @@ -163,6 +166,7 @@ def generate_dockerfile(yaml_config: Dict[str, Any]) -> str: # Common configuration for all images dockerfile += generate_common_env_config() dockerfile += generate_install_common_packages() + dockerfile += generate_python() dockerfile += generate_locale() dockerfile += generate_cmake(_get_package_version(yaml_config, "cmake")) dockerfile += generate_terminator() @@ -208,6 +212,8 @@ def generate_dockerfile(yaml_config: Dict[str, Any]) -> str: # Add ROS dockerfile += generate_ros(yaml_config["ros_version"]) + dockerfile += generate_ros_extra(yaml_config["ros_version"]) + dockerfile += generate_ros_autocomplete_fix(yaml_config["ros_version"]) # Add the extra-packages extra_packages_label_list = list() diff --git a/turludock/generate_non_templated_files.py b/turludock/generate_non_templated_files.py index 118c2a9..4077862 100644 --- a/turludock/generate_non_templated_files.py +++ b/turludock/generate_non_templated_files.py @@ -55,6 +55,15 @@ def generate_install_common_packages() -> str: return get_non_templated_file("install_common_packages.txt") +def generate_python() -> str: + """Get python.txt as a string + + Returns: + str: The python.txt as a string + """ + return get_non_templated_file("python.txt") + + def generate_locale() -> str: """Get locale.txt as a string diff --git a/turludock/generate_templated_files.py b/turludock/generate_templated_files.py index d710886..b7ddf9e 100644 --- a/turludock/generate_templated_files.py +++ b/turludock/generate_templated_files.py @@ -13,7 +13,6 @@ get_cpu_count_for_build, get_ros_major_version, get_ubuntu_version, - is_ros_version_supported, is_version_greater, is_version_lower, ) @@ -29,11 +28,18 @@ def populate_templated_file(mapping: Dict[str, str], templated_file: str) -> str Returns: str: The populated templated file. """ - with importlib.resources.open_text("turludock.assets.dockerfile_templates", templated_file) as f: - src = Template(f.read()) - str_output = src.substitute(mapping) - str_output += "\n\n" - return str_output + try: + with importlib.resources.open_text("turludock.assets.dockerfile_templates", templated_file) as f: + src = Template(f.read()) + str_output = src.substitute(mapping) + str_output += "\n\n" + return str_output + except FileNotFoundError: + print(f"Template file '{templated_file}' not found.") + except KeyError as e: + print(f"Missing substitution key: {e}") + except Exception as e: + print(f"An unexpected error occurred at function `populate_templated_file`: {e}") def _get_ubuntu_base_image(version: str, nvidia: bool) -> str: @@ -121,10 +127,6 @@ def generate_header_info(docker_label_description: str, ros_version_short: str) """ logger.debug(f"Generate 'header_info.txt'. Input: {docker_label_description}, {ros_version_short}") - # Check if provided version is supported - if not is_ros_version_supported(ros_version_short): - raise ValueError(f"ROS version '{ros_version_short}' not supported. Check your configuration.") - # Map the template variables mapping = { "docker_label_description": docker_label_description, @@ -199,7 +201,7 @@ def generate_llvm(version: str) -> str: def generate_ros(ros_version_codename: str) -> str: - """Generates the 'ros1.txt' or 'ros2.txt' templated file, which is responsible for installing ROS 1 or 2 + """Generates the 'rosX.txt' templated file, which is responsible for installing ROS 1 or 2 The selection of ROS 1 or 2 is based on the provided ROS codename. @@ -207,10 +209,7 @@ def generate_ros(ros_version_codename: str) -> str: ros_version_codename (str): The ROS version as codename. Returns: - str: The populated 'ros1.txt' file as a string. - - Raises: - ValueError: If the provided ROS version is not supported. + str: The populated 'rosX.txt' file as a string. """ # Determine if it ROS1 or ROS2 if get_ros_major_version(ros_version_codename) == 1: @@ -220,9 +219,32 @@ def generate_ros(ros_version_codename: str) -> str: logger.debug(f"Generate '{template_file}'. Input: {ros_version_codename}") - # Check if provided version is supported - if not is_ros_version_supported(ros_version_codename): - raise ValueError("ROS version not supported. Check your configuration.") + # Map the template variables + mapping = {"ros_version_short": ros_version_codename} + + # Populate the templated file + return populate_templated_file(mapping, template_file) + + +def generate_ros_extra(ros_version_codename: str) -> str: + """Generates the 'rosX_extra.txt' templated file, which is responsible for installing extra ROS packages + + The selection of ROS 1 or 2 is based on the provided ROS codename. + + Args: + ros_version_codename (str): The ROS version as codename. + + Returns: + str: The populated 'rosX_extra.txt' file as a string. + """ + # Determine if it ROS1 or ROS2 + if get_ros_major_version(ros_version_codename) == 1: + # TODO + return "" + else: + template_file = "ros2_extra.txt" + + logger.debug(f"Generate '{template_file}'. Input: {ros_version_codename}") # Map the template variables mapping = {"ros_version_short": ros_version_codename} @@ -231,6 +253,45 @@ def generate_ros(ros_version_codename: str) -> str: return populate_templated_file(mapping, template_file) +def generate_ros_autocomplete_fix(ros_version_codename: str) -> str: + """Generates the 'ros2_autocomplete_fix.txt' templated file as string + + This file is responsible for introducing a fix for zsh autocomplete. + https://github.com/ros2/ros2cli/issues/534#issuecomment-957516107 + + This has the hidden assumption that a ROS2 version is always coupled to a Ubuntu version. + The fix should actually be on the Ubuntu version and not the ROS2 version. + + Args: + ros_version_codename (str): The ROS version as codename. + + Returns: + str: The populated 'ros2_autocomplete_fix.txt' file as a string. + + Raises: + ValueError: If ROS version not supported / not yet added by this function + """ + + if get_ros_major_version(ros_version_codename) == 1: + return "" + + template_file = "ros2_autocomplete_fix.txt" + + # Only apply for Humble + if ros_version_codename == "humble" or ros_version_codename == "iron": + mapping = {"python_argcomplete": "python-argcomplete3"} + elif ros_version_codename == "jazzy": + mapping = {"python_argcomplete": "python-argcomplete"} + else: + raise ValueError( + f"ROS version {ros_version_codename} not yet supported by this function. " + "Ask the developer to add support!" + ) + + logger.debug(f"Generate '{template_file}'. Input: {ros_version_codename}") + return populate_templated_file(mapping, template_file) + + def generate_extra_packages_label(extra_packages_list: List[str]) -> str: """Generates the 'extra_packages_label.txt' templated file