diff --git a/.github/workflows/justina-colombia.yml b/.github/workflows/justina-colombia.yml new file mode 100644 index 000000000..f1853be14 --- /dev/null +++ b/.github/workflows/justina-colombia.yml @@ -0,0 +1,48 @@ +name: Validate JUSTINA Colombia proposal + +on: + pull_request: + paths: + - "catkin_ws/Justina.workspace" + - "dockers/justina-colombia/**" + - ".github/workflows/justina-colombia.yml" + workflow_dispatch: + +permissions: + contents: read + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Verify ROS workspace distribution + shell: bash + run: grep -Fq '' catkin_ws/Justina.workspace + + - name: Verify Colombia locale override + shell: bash + run: grep -Fq 'LC_ALL=es_CO.UTF-8' dockers/justina-colombia/Dockerfile + + - name: Check shell scripts + shell: bash + run: | + bash -n dockers/justina-colombia/container-entrypoint.sh + bash -n dockers/justina-colombia/build-workspace.sh + shellcheck dockers/justina-colombia/container-entrypoint.sh + shellcheck dockers/justina-colombia/build-workspace.sh + + - name: Reject unsafe legacy patterns + shell: bash + run: | + ! grep -RE -- '--privileged|apt-key|nvidia-docker|chown[[:space:]]+-R[[:space:]]+/home|rm[[:space:]]+-rf[[:space:]]+/opt/ros' \ + dockers/justina-colombia/Dockerfile \ + dockers/justina-colombia/compose.yaml \ + dockers/justina-colombia/container-entrypoint.sh + + - name: Validate Compose configuration + working-directory: dockers/justina-colombia + env: + JUSTINA_REPO_PATH: ${{ github.workspace }} + run: docker compose config diff --git a/.gitignore b/.gitignore index 1e1913d8d..5a35bc5cf 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ *.swn catkin_ws/devel/ catkin_ws/build/ +catkin_ws/logs/ +catkin_ws/.catkin_tools/ catkin_ws/src/CMakeLists.txt catkin_ws/src/hri/justina_gui/src/justina_gui-release/* !catkin_ws/src/hri/justina_gui/src/justina_gui-release/ui_MainWindow.h @@ -81,3 +83,6 @@ catkin_ws/src/vision/obj_reco_plastic_tray/\.kdev4/ catkin_ws/src/src\.kdev4 catkin_ws/src/\.kdev4/ + +# Local container configuration +dockers/justina-colombia/.env diff --git a/catkin_ws/Justina.workspace b/catkin_ws/Justina.workspace index e1eaf93b6..849213875 100644 --- a/catkin_ws/Justina.workspace +++ b/catkin_ws/Justina.workspace @@ -1,6 +1,6 @@ - + src diff --git a/catkin_ws/src/hardware/head/package.xml b/catkin_ws/src/hardware/head/package.xml index 0c083837b..f0f512e9e 100644 --- a/catkin_ws/src/hardware/head/package.xml +++ b/catkin_ws/src/hardware/head/package.xml @@ -41,11 +41,15 @@ catkin geometry_msgs + hardware_tools + message_generation roscpp rospy std_msgs sensor_msgs geometry_msgs + hardware_tools + message_runtime roscpp rospy std_msgs diff --git a/dockers/justina-colombia/.env.example b/dockers/justina-colombia/.env.example new file mode 100644 index 000000000..45ba24820 --- /dev/null +++ b/dockers/justina-colombia/.env.example @@ -0,0 +1,11 @@ +# Ruta absoluta al clon local de RobotJustina/JUSTINA. +JUSTINA_REPO_PATH=/ruta/a/JUSTINA + +# Imagen conservadora por defecto. Puede apuntar a un registro interno. +ROS_BASE_IMAGE=ros:kinetic-ros-base-xenial + +# Use los identificadores del usuario anfitrión para evitar archivos propiedad de root. +USER_UID=1000 +USER_GID=1000 + +ROS_MASTER_URI=http://localhost:11311 diff --git a/dockers/justina-colombia/Dockerfile b/dockers/justina-colombia/Dockerfile new file mode 100644 index 000000000..e600d5622 --- /dev/null +++ b/dockers/justina-colombia/Dockerfile @@ -0,0 +1,66 @@ +ARG ROS_BASE_IMAGE=ros:kinetic-ros-base-xenial +FROM ${ROS_BASE_IMAGE} + +ARG DEBIAN_FRONTEND=noninteractive +ARG USERNAME=justina +ARG USER_UID=1000 +ARG USER_GID=1000 + +ENV TZ=America/Bogota \ + LANG=es_CO.UTF-8 \ + LANGUAGE=es_CO:es \ + LC_ALL=es_CO.UTF-8 \ + LC_TIME=es_CO.UTF-8 \ + ROS_DISTRO=kinetic \ + JUSTINA_WS=/workspace/JUSTINA/catkin_ws + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + bash-completion \ + build-essential \ + ca-certificates \ + git \ + libjack-jackd2-dev \ + libopencv-dev \ + libpcl-dev \ + locales \ + python-catkin-tools \ + python-rosdep \ + qtbase5-dev \ + ros-kinetic-amcl \ + ros-kinetic-desktop-full \ + ros-kinetic-dynamixel-sdk \ + ros-kinetic-fake-localization \ + ros-kinetic-gmapping \ + ros-kinetic-joy \ + ros-kinetic-laser-geometry \ + ros-kinetic-map-server \ + ros-kinetic-pcl-ros \ + ros-kinetic-pointcloud-to-laserscan \ + ros-kinetic-serial \ + ros-kinetic-sound-play \ + ros-kinetic-tf \ + ros-kinetic-tf2-bullet \ + ros-kinetic-tf-conversions \ + ros-kinetic-urg-node \ + sudo \ + tzdata \ + && locale-gen es_CO.UTF-8 \ + && update-locale LANG=es_CO.UTF-8 LANGUAGE=es_CO:es LC_TIME=es_CO.UTF-8 \ + && ln -snf /usr/share/zoneinfo/America/Bogota /etc/localtime \ + && echo America/Bogota > /etc/timezone \ + && groupadd --gid "${USER_GID}" "${USERNAME}" \ + && useradd --uid "${USER_UID}" --gid "${USER_GID}" --create-home --shell /bin/bash "${USERNAME}" \ + && echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${USERNAME}" \ + && chmod 0440 "/etc/sudoers.d/${USERNAME}" \ + && rm -rf /var/lib/apt/lists/* + +COPY container-entrypoint.sh /usr/local/bin/container-entrypoint +COPY build-workspace.sh /usr/local/bin/build-justina-workspace +RUN chmod 0755 /usr/local/bin/container-entrypoint /usr/local/bin/build-justina-workspace + +USER justina +WORKDIR /workspace/JUSTINA + +ENTRYPOINT ["/usr/local/bin/container-entrypoint"] +CMD ["bash"] diff --git a/dockers/justina-colombia/README.md b/dockers/justina-colombia/README.md new file mode 100644 index 000000000..23f941f60 --- /dev/null +++ b/dockers/justina-colombia/README.md @@ -0,0 +1,82 @@ +# Propuesta de contenedor JUSTINA para Colombia + +Esta propuesta conserva Ubuntu 16.04 y ROS Kinetic dentro de un contenedor. No +ejecuta los instaladores heredados ni modifica paquetes del equipo anfitrión. + +## Alcance + +- Usa la imagen legada `ros:kinetic-ros-base-xenial` e instala el entorno + completo `ros-kinetic-desktop-full`. +- Declara explícitamente la plataforma `linux/amd64`. +- Configura `America/Bogota` y `es_CO.UTF-8`. +- Monta el clon de JUSTINA en `/workspace/JUSTINA`. +- Ejecuta como usuario sin privilegios. +- No incluye CUDA, cámaras, USB ni acceso privilegiado por defecto. + +La imagen depende de componentes sin soporte. Debe usarse para recuperación, +evaluación y migración, no como plataforma nueva de producción. +Si la imagen pública deja de estar disponible, configure `ROS_BASE_IMAGE` para +usar una copia aprobada en un registro interno. + +## Uso + +1. Entre a `dockers/justina-colombia`. +2. Cree la configuración local: + + ```bash + cd dockers/justina-colombia + cp .env.example .env + ``` + +3. Cambie `JUSTINA_REPO_PATH` en `.env` por la ruta absoluta del repositorio. +4. Construya y abra el entorno: + + ```bash + docker compose build + docker compose run --rm justina + ``` + +5. Dentro del contenedor, prepare dependencias y compile el workspace: + + ```bash + build-justina-workspace + ``` + +El comando incluye distribuciones ROS fuera de soporte al actualizar `rosdep`, +instala las dependencias declaradas, limpia resultados anteriores y compila. +Omite `libopenni-sensor-primesense-dev` porque ya no está disponible en los +repositorios de Ubuntu Xenial. Los módulos compilan con `libopenni`, pero usar +un sensor PrimeSense antiguo requiere instalar su controlador manualmente. + +En Windows, después de aplicar la propuesta, valide los archivos con: + +```powershell +.\dockers\justina-colombia\validate-proposal.ps1 -RepositoryPath . +``` + +## Issue #13 + +El archivo `catkin_ws/Justina.workspace` declara ROS Indigo, mientras los scripts +del repositorio instalan ROS Kinetic. Esta propuesta actualiza esa metadata a +Kinetic. + +## Hardware y GPU + +No habilite `--privileged` como solución general. Agregue dispositivos concretos +solo después de identificarlos, por ejemplo `/dev/video0` o un puerto serial. + +Para GPU, instale NVIDIA Container Toolkit en el anfitrión y cree una variante +separada de Compose. CUDA 8 y las GPU modernas pueden ser incompatibles. + +## Validación antes de revisión + +```bash +docker compose config +docker build --check . +shellcheck container-entrypoint.sh +grep -Fq '' ../../catkin_ws/Justina.workspace +``` + +Estas validaciones requieren Docker Compose v2 y un anfitrión Linux. Docker +Desktop para Windows puede inspeccionar el archivo, pero `network_mode: host` y +el montaje de X11 están diseñados para Linux. diff --git a/dockers/justina-colombia/build-workspace.sh b/dockers/justina-colombia/build-workspace.sh new file mode 100644 index 000000000..02b7a3576 --- /dev/null +++ b/dockers/justina-colombia/build-workspace.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +source "/opt/ros/${ROS_DISTRO}/setup.bash" + +if [[ ! -f /etc/ros/rosdep/sources.list.d/20-default.list ]]; then + sudo rosdep init +fi + +rosdep update --include-eol-distros +rosdep install \ + --from-paths "${JUSTINA_WS}/src" \ + --ignore-src \ + --rosdistro "${ROS_DISTRO}" \ + --skip-keys "libopenni-sensor-primesense-dev" \ + -r \ + -y + +cd "${JUSTINA_WS}" +catkin clean -y +catkin build diff --git a/dockers/justina-colombia/compose.yaml b/dockers/justina-colombia/compose.yaml new file mode 100644 index 000000000..10b963937 --- /dev/null +++ b/dockers/justina-colombia/compose.yaml @@ -0,0 +1,23 @@ +services: + justina: + platform: linux/amd64 + build: + context: . + dockerfile: Dockerfile + args: + ROS_BASE_IMAGE: "${ROS_BASE_IMAGE:-ros:kinetic-ros-base-xenial}" + USER_UID: "${USER_UID:-1000}" + USER_GID: "${USER_GID:-1000}" + image: justina-colombia:kinetic + container_name: justina-colombia + environment: + DISPLAY: "${DISPLAY:-}" + ROS_MASTER_URI: "${ROS_MASTER_URI:-http://localhost:11311}" + TZ: America/Bogota + network_mode: host + stdin_open: true + tty: true + volumes: + - "${JUSTINA_REPO_PATH:?Defina JUSTINA_REPO_PATH}:/workspace/JUSTINA" + - "/tmp/.X11-unix:/tmp/.X11-unix:rw" + working_dir: /workspace/JUSTINA diff --git a/dockers/justina-colombia/container-entrypoint.sh b/dockers/justina-colombia/container-entrypoint.sh new file mode 100644 index 000000000..c333e9636 --- /dev/null +++ b/dockers/justina-colombia/container-entrypoint.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +source "/opt/ros/${ROS_DISTRO}/setup.bash" + +if [[ -f "${JUSTINA_WS}/devel/setup.bash" ]]; then + source "${JUSTINA_WS}/devel/setup.bash" +fi + +exec "$@" diff --git a/dockers/justina-colombia/validate-proposal.ps1 b/dockers/justina-colombia/validate-proposal.ps1 new file mode 100644 index 000000000..0a8f31fc6 --- /dev/null +++ b/dockers/justina-colombia/validate-proposal.ps1 @@ -0,0 +1,57 @@ +[CmdletBinding()] +param( + [string]$RepositoryPath = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path +) + +$ErrorActionPreference = 'Stop' +$proposalDirectory = $PSScriptRoot +$workspaceFile = Join-Path $RepositoryPath 'catkin_ws\Justina.workspace' +$composeFile = Join-Path $proposalDirectory 'compose.yaml' +$dockerfile = Join-Path $proposalDirectory 'Dockerfile' + +if (-not (Test-Path -LiteralPath $workspaceFile -PathType Leaf)) { + throw "No se encontró el workspace: $workspaceFile" +} + +[xml]$workspace = Get-Content -Raw -LiteralPath $workspaceFile +$distribution = $workspace.Workspace.Distribution.name +if ($distribution -ne 'kinetic') { + throw "Se esperaba ROS Kinetic en Justina.workspace; se encontró: $distribution" +} + +$dockerfileContent = Get-Content -Raw -LiteralPath $dockerfile +if ($dockerfileContent -notmatch 'LC_ALL=es_CO\.UTF-8') { + throw 'El Dockerfile no configura LC_ALL=es_CO.UTF-8.' +} + +$unsafePatterns = @('--privileged', 'apt-key', 'nvidia-docker', 'chown\s+-R\s+/home', 'rm\s+-rf\s+/opt/ros') +$implementationFiles = @( + $composeFile, + $dockerfile, + (Join-Path $proposalDirectory 'container-entrypoint.sh'), + (Join-Path $proposalDirectory 'build-workspace.sh') +) +foreach ($pattern in $unsafePatterns) { + if (Select-String -Path $implementationFiles -Pattern $pattern) { + throw "Se encontró un patrón heredado no permitido: $pattern" + } +} + +$previousRepoPath = $env:JUSTINA_REPO_PATH +try { + $env:JUSTINA_REPO_PATH = $RepositoryPath + if (Get-Command docker-compose -ErrorAction SilentlyContinue) { + & docker-compose -f $composeFile config | Out-Null + } + else { + & docker compose -f $composeFile config | Out-Null + } + if ($LASTEXITCODE -ne 0) { + throw 'La validación de Docker Compose falló.' + } +} +finally { + $env:JUSTINA_REPO_PATH = $previousRepoPath +} + +Write-Host 'Validación de la propuesta JUSTINA Colombia: correcta.'