From 6389c1a82bf8a7ba8320b99ae3737fd97c7cb321 Mon Sep 17 00:00:00 2001 From: Shehab Attia Date: Thu, 4 Jun 2026 12:51:37 -0400 Subject: [PATCH 1/5] Added a Dockerfile to run stretch_new_robot_install.sh in a Docker container. Added a GitHub Action to run on a PR to master --- .github/docker/Dockerfile | 113 +++++++++++++++++++ .github/docker/README.md | 60 +++++++++++ .github/docker/build.sh | 109 +++++++++++++++++++ .github/docker/common.sh | 58 ++++++++++ .github/docker/test.sh | 138 ++++++++++++++++++++++++ .github/workflows/test_with_docker.yaml | 58 ++++++++++ 6 files changed, 536 insertions(+) create mode 100644 .github/docker/Dockerfile create mode 100644 .github/docker/README.md create mode 100755 .github/docker/build.sh create mode 100755 .github/docker/common.sh create mode 100755 .github/docker/test.sh create mode 100644 .github/workflows/test_with_docker.yaml diff --git a/.github/docker/Dockerfile b/.github/docker/Dockerfile new file mode 100644 index 0000000..3e8b130 --- /dev/null +++ b/.github/docker/Dockerfile @@ -0,0 +1,113 @@ +# Dockerfile for Stretch Robot Installation +# Can be executed in GitHub Actions workflows or manually +# Usage: +# docker build -t stretch-install -f .github/docker/Dockerfile . +# docker run --rm stretch-install + +FROM ubuntu:22.04 + +# Prevent interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC + +# Set the FLEET_ID environment variable +# This will be used by the installation scripts +ARG SETUP_FLEET_ID=stretch-se3-0000 +ENV SETUP_FLEET_ID=${SETUP_FLEET_ID} + +# Install basic dependencies required by the installation scripts +# Including iputils-ping for network connectivity checks and zip for logs packaging +RUN apt-get update && apt-get install -y \ + sudo \ + git \ + curl \ + wget \ + ca-certificates \ + gnupg \ + lsb-release \ + iputils-ping \ + expect \ + zip \ + software-properties-common \ + && rm -rf /var/lib/apt/lists/* + +# Pre-create necessary directories that don't exist by default in basic Docker images +# Also mock udevadm and update-initramfs which are not useful inside Docker and fail/warn in container environments +RUN mkdir -p /etc/udev/rules.d /etc/modprobe.d /etc/apt/keyrings && \ + printf '#!/bin/sh\nexit 0\n' > /usr/sbin/update-initramfs && \ + chmod +x /usr/sbin/update-initramfs && \ + printf '#!/bin/sh\nexit 0\n' > /usr/bin/udevadm && \ + chmod +x /usr/bin/udevadm + +# Create a non-root user 'hello-robot' with sudo privileges +# The installation scripts expect to run as a regular user with sudo access +RUN useradd -m -s /bin/bash hello-robot && \ + echo "hello-robot ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +# Switch to the hello-robot user +USER hello-robot +WORKDIR /home/hello-robot + +# Clone the stretch_install repository to the container +# This must be in the home directory as expected by the scripts +RUN git clone https://github.com/hello-robot/stretch_install /home/hello-robot/stretch_install + +# Patch the stretch_initial_setup.sh script to skip git checks and network connectivity checks in Docker +# This is necessary because: +# 1. The git remote check won't work in a Docker build context (no .git directory is copied due to .dockerignore) +# 2. Network connectivity checks (pinging google.com) may fail in some Docker build environments +RUN cd /home/hello-robot/stretch_install/factory/22.04 && \ + cp stretch_initial_setup.sh stretch_initial_setup.sh.orig && \ + sed -i '/^echo "Checking install repo is up-to-date..."/,/^fi$/d' stretch_initial_setup.sh && \ + sed -i '/^echo "Waiting to get online..."/,/^done$/c\echo "Skipping network check in Docker environment..."' stretch_initial_setup.sh + +# Create the calibration data directory structure +# The installation script expects calibration data in $HOME/ +# For Docker builds, we'll create a minimal structure to satisfy the script +RUN mkdir -p /home/hello-robot/${SETUP_FLEET_ID}/udev && \ + echo '# Minimal udev rules for Docker build' > /home/hello-robot/${SETUP_FLEET_ID}/udev/stretch-device.rules && \ + chmod -R a+r /home/hello-robot/${SETUP_FLEET_ID} + +# Pre-create required directories to ensure proper permissions +RUN mkdir -p /home/hello-robot/stretch_user/log && \ + mkdir -p /home/hello-robot/stretch_user/debug && \ + mkdir -p /home/hello-robot/stretch_user/maps && \ + mkdir -p /home/hello-robot/stretch_user/models && \ + mkdir -p /home/hello-robot/.local/bin + +# Set up error handling for the installation +# The script already uses 'set -e' and 'set -o pipefail' for error propagation +# We'll use shell options to ensure any errors cause the build to fail +SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] + +# Run the installation script with the FLEET_ID environment variable +# The -H flag passes the complete fleet ID to the script +# We redirect stderr to stdout to capture all output +# The script will exit with non-zero code on failure, causing the build to fail +RUN cd /home/hello-robot/stretch_install && \ + bash -c 'yes | ./stretch_new_robot_install.sh -H ${SETUP_FLEET_ID} 2>&1 | tee /home/hello-robot/stretch_user/log/docker_install.log; exit ${PIPESTATUS[0]}' + +# Verify that the FLEET_ID was properly configured in both locations +RUN if [ ! -f /etc/hello-robot/hello-robot.conf ]; then \ + echo "ERROR: /etc/hello-robot/hello-robot.conf not found"; \ + exit 1; \ + fi && \ + if ! grep -q "HELLO_FLEET_ID=${SETUP_FLEET_ID}" /etc/hello-robot/hello-robot.conf; then \ + echo "ERROR: FLEET_ID not properly configured in /etc/hello-robot/hello-robot.conf"; \ + cat /etc/hello-robot/hello-robot.conf; \ + exit 1; \ + fi && \ + if [ ! -d /home/hello-robot/stretch_user/${SETUP_FLEET_ID} ]; then \ + echo "ERROR: /home/hello-robot/stretch_user/${SETUP_FLEET_ID} directory not found"; \ + exit 1; \ + fi && \ + echo "SUCCESS: FLEET_ID properly configured in all required locations" + +# Set the default command to display installation success and fleet ID +CMD ["/bin/bash", "-c", "echo 'Stretch Robot Installation Complete' && \ + echo 'Fleet ID: '${SETUP_FLEET_ID} && \ + echo 'Configuration verified in:' && \ + echo ' - /etc/hello-robot/hello-robot.conf' && \ + echo ' - /home/hello-robot/stretch_user/'${SETUP_FLEET_ID} && \ + /bin/bash"] + diff --git a/.github/docker/README.md b/.github/docker/README.md new file mode 100644 index 0000000..27bc327 --- /dev/null +++ b/.github/docker/README.md @@ -0,0 +1,60 @@ +# Docker-Based Stretch Installation Testing + +This directory contains utility scripts and configurations for building and testing the Stretch Robot Installation script (`stretch_new_robot_install.sh`) in an isolated Docker container. + +A GitHub Actions workflow is also configured to run these tests automatically on push or pull requests to the `master` branch. + +--- + +## 1. Running Tests Locally + +You can run the build and verification steps locally on any system with Docker installed. + +### Step A: Build the Docker Image +The `build.sh` script handles building the Docker image. It accepts a specific robot Fleet ID to mock the calibration and configuration files. + +To build the default image (`stretch-install:stretch-se3-0000`): +```bash +./.github/docker/build.sh +``` + +To build for a specific model (e.g., `stretch-re2-0001`): +```bash +./.github/docker/build.sh -f stretch-re2-0001 +``` + +To build with a custom image name: +```bash +./.github/docker/build.sh -f stretch-re2-0001 -n custom-stretch-test +``` + +### Step B: Run the Verification Tests +The `test.sh` script runs a suite of 12 verification checks (e.g., checking configuration files, installed packages, ROS Humble installation, directory structures, and log status) against the built image. + +To test the default image: +```bash +./.github/docker/test.sh -i stretch-install:stretch-se3-0000 +``` + +To test a custom-named image: +```bash +./.github/docker/test.sh -i custom-stretch-test:stretch-re2-0001 +``` + +--- + +## 2. Shared Utilities (`common.sh`) +Both scripts source [common.sh](./common.sh) for shared configurations: +* **Color Definitions & Log Handlers**: Provides unified log output styling (`[INFO]`, `[PASS]`, `[FAIL]`, etc.). +* **Docker Check**: Detects if your user is in the `docker` group and automatically prepends `sudo` to docker commands if root permissions are required. + +--- + +## 3. Automated GitHub Actions Workflow +The workflow file at [test-installation.yaml](../workflows/test-installation.yaml) runs these same scripts automatically in GitHub Actions for every pull request and push to `master`. + +It uses a matrix strategy to test multiple models (e.g., `stretch-se3-0000` and `stretch-re2-0001`) concurrently: +1. Checks out the repository. +2. Sets up Docker Buildx. +3. Invokes `build.sh` to compile the Docker image. +4. Invokes `test.sh` to run the verification tests. diff --git a/.github/docker/build.sh b/.github/docker/build.sh new file mode 100755 index 0000000..4f14ff0 --- /dev/null +++ b/.github/docker/build.sh @@ -0,0 +1,109 @@ +#!/bin/bash +# Build script for Stretch Robot Installation Docker image +# This script can be used to build and test the Docker image locally + +set -e + +# Default values +FLEET_ID="${FLEET_ID:-stretch-se3-0000}" +IMAGE_NAME="${IMAGE_NAME:-stretch-install}" +DOCKERFILE_PATH=".github/docker/Dockerfile" +BUILD_CONTEXT="." + +# Source common utilities +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [ -f "$SCRIPT_DIR/common.sh" ]; then + source "$SCRIPT_DIR/common.sh" +else + echo "Error: common.sh not found in $SCRIPT_DIR" + exit 1 +fi + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -f|--fleet-id) + FLEET_ID="$2" + shift 2 + ;; + -n|--name) + IMAGE_NAME="$2" + shift 2 + ;; + -h|--help) + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " -f, --fleet-id ID Set the fleet ID (default: stretch-se3-0000)" + echo " -n, --name NAME Set the image name (default: stretch-install)" + echo " -h, --help Display this help message" + echo "" + echo "Environment Variables:" + echo " FLEET_ID Set the fleet ID (overridden by -f flag)" + echo " IMAGE_NAME Set the image name (overridden by -n flag)" + echo "" + echo "Examples:" + echo " $0" + echo " $0 -f stretch-re2-1234" + echo " $0 -f stretch-se3-0001 -n my-stretch-install" + echo " FLEET_ID=stretch-re2-5678 $0" + exit 0 + ;; + *) + print_error "Unknown option: $1" + echo "Run '$0 --help' for usage information" + exit 1 + ;; + esac +done + +# Validate fleet ID format +if [[ ! $FLEET_ID =~ ^stretch-(re1|re2|se3)-[0-9]{4}$ ]]; then + print_error "Invalid fleet ID format: $FLEET_ID" + echo "Fleet ID must match the format: stretch-(re1|re2|se3)-XXXX" + echo "where XXXX is a 4-digit number" + exit 1 +fi + +print_info "Building Stretch Robot Installation Docker image" +print_info "Fleet ID: $FLEET_ID" +print_info "Image name: $IMAGE_NAME" +print_info "Dockerfile: $DOCKERFILE_PATH" +echo "" + +# Check if we're in the right directory +if [ ! -f "$DOCKERFILE_PATH" ]; then + print_error "Dockerfile not found at $DOCKERFILE_PATH" + print_error "Please run this script from the repository root directory" + exit 1 +fi + +# Determine if we need to use sudo for docker +detect_docker + +# Build the Docker image +print_info "Starting Docker build..." +if $DOCKER_CMD build \ + --build-arg SETUP_FLEET_ID="$FLEET_ID" \ + -t "$IMAGE_NAME:$FLEET_ID" \ + -t "$IMAGE_NAME:latest" \ + -f "$DOCKERFILE_PATH" \ + "$BUILD_CONTEXT"; then + + print_info "Docker build completed successfully!" + echo "" + print_info "Image tags:" + echo " - $IMAGE_NAME:$FLEET_ID" + echo " - $IMAGE_NAME:latest" + echo "" + print_info "To run the container:" + echo " docker run -it --rm $IMAGE_NAME:latest" + echo "" + print_info "To verify the installation:" + echo " docker run --rm $IMAGE_NAME:latest cat /etc/hello-robot/hello-robot.conf" + echo "" +else + print_error "Docker build failed!" + exit 1 +fi + diff --git a/.github/docker/common.sh b/.github/docker/common.sh new file mode 100755 index 0000000..9258148 --- /dev/null +++ b/.github/docker/common.sh @@ -0,0 +1,58 @@ +#!/bin/bash +# Common utilities for Stretch Robot Docker build and test scripts + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Function to print colored output +print_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +print_success() { + echo -e "${GREEN}[PASS]${NC} $1" +} + +print_failure() { + echo -e "${RED}[FAIL]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +print_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +# Unified Docker command/privilege detection +DOCKER_CMD="" +detect_docker() { + if [ -n "$DOCKER_CMD" ]; then + return 0 + fi + + DOCKER_CMD="docker" + if ! docker ps >/dev/null 2>&1; then + # Try with sudo + print_warning "Docker requires elevated privileges. Checking with sudo..." + if sudo -n docker ps >/dev/null 2>&1; then + DOCKER_CMD="sudo docker" + print_warning "Using sudo for Docker commands (user not in docker group)" + else + # Need password for sudo + print_warning "Please enter your password for sudo access to Docker" + if sudo docker ps >/dev/null 2>&1; then + DOCKER_CMD="sudo docker" + print_warning "Using sudo for Docker commands (user not in docker group)" + else + print_error "Docker is not running or not accessible" + exit 1 + fi + fi + fi +} diff --git a/.github/docker/test.sh b/.github/docker/test.sh new file mode 100755 index 0000000..b0f547e --- /dev/null +++ b/.github/docker/test.sh @@ -0,0 +1,138 @@ +#!/bin/bash +# Test script for Stretch Robot Installation Docker image +# This script verifies that the Docker installation completed successfully + +set -e + +# Default values +IMAGE_NAME="${IMAGE_NAME:-stretch-install:latest}" + +# Source common utilities +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +if [ -f "$SCRIPT_DIR/common.sh" ]; then + source "$SCRIPT_DIR/common.sh" +else + echo "Error: common.sh not found in $SCRIPT_DIR" + exit 1 +fi + +# Test counters +TESTS_PASSED=0 +TESTS_FAILED=0 + +# Function to run a test +run_test() { + local test_name="$1" + local test_command="$2" + + print_info "Running test: $test_name" + + if eval "$test_command"; then + print_success "$test_name" + ((TESTS_PASSED++)) + return 0 + else + print_failure "$test_name" + ((TESTS_FAILED++)) + return 1 + fi +} + +# Parse command line arguments +while [[ $# -gt 0 ]]; do + case $1 in + -i|--image) + IMAGE_NAME="$2" + shift 2 + ;; + -h|--help) + echo "Usage: $0 [options]" + echo "" + echo "Options:" + echo " -i, --image NAME Set the image name to test (default: stretch-install:latest)" + echo " -h, --help Display this help message" + echo "" + echo "Examples:" + echo " $0" + echo " $0 -i stretch-install:stretch-se3-0000" + exit 0 + ;; + *) + print_failure "Unknown option: $1" + echo "Run '$0 --help' for usage information" + exit 1 + ;; + esac +done + +# Detect docker command +detect_docker + +print_info "Testing Stretch Robot Installation Docker image: $IMAGE_NAME" +echo "" + +# Test 1: Check if image exists +run_test "Image exists" \ + "$DOCKER_CMD image inspect $IMAGE_NAME > /dev/null 2>&1" + +# Test 2: Check if /etc/hello-robot/hello-robot.conf exists +run_test "Configuration file exists" \ + "$DOCKER_CMD run --rm $IMAGE_NAME test -f /etc/hello-robot/hello-robot.conf" + +# Test 3: Check if HELLO_FLEET_ID is set in configuration +run_test "FLEET_ID configured in /etc/hello-robot/hello-robot.conf" \ + "$DOCKER_CMD run --rm $IMAGE_NAME grep -q 'HELLO_FLEET_ID=' /etc/hello-robot/hello-robot.conf" + +# Test 4: Check if stretch_user directory exists +run_test "stretch_user directory exists" \ + "$DOCKER_CMD run --rm $IMAGE_NAME test -d /home/hello-robot/stretch_user" + +# Test 5: Check if fleet-specific directory exists in stretch_user +run_test "Fleet-specific directory exists in stretch_user" \ + "$DOCKER_CMD run --rm $IMAGE_NAME bash -c 'FLEET_ID=\$(grep HELLO_FLEET_ID /etc/hello-robot/hello-robot.conf | cut -d= -f2) && test -d /home/hello-robot/stretch_user/\$FLEET_ID'" + +# Test 6: Check if log directory exists +run_test "Log directory exists" \ + "$DOCKER_CMD run --rm $IMAGE_NAME test -d /home/hello-robot/stretch_user/log" + +# Test 7: Check if installation log was created +run_test "Installation log exists" \ + "$DOCKER_CMD run --rm $IMAGE_NAME test -f /home/hello-robot/stretch_user/log/docker_install.log" + +# Test 8: Check if Python is installed +run_test "Python3 is installed" \ + "$DOCKER_CMD run --rm $IMAGE_NAME which python3" + +# Test 9: Check if pip is installed +run_test "pip3 is installed" \ + "$DOCKER_CMD run --rm $IMAGE_NAME which pip3" + +# Test 10: Check if git is installed +run_test "git is installed" \ + "$DOCKER_CMD run --rm $IMAGE_NAME which git" + +# Test 11: Check if .bashrc was updated +run_test ".bashrc contains HELLO_FLEET_ID" \ + "$DOCKER_CMD run --rm $IMAGE_NAME grep -q 'HELLO_FLEET_ID' /home/hello-robot/.bashrc" + +# Test 12: Check if .local/bin directory exists +run_test ".local/bin directory exists" \ + "$DOCKER_CMD run --rm $IMAGE_NAME test -d /home/hello-robot/.local/bin" + +echo "" +echo "========================================" +echo "Test Summary" +echo "========================================" +echo -e "${GREEN}Passed:${NC} $TESTS_PASSED" +echo -e "${RED}Failed:${NC} $TESTS_FAILED" +echo "Total: $((TESTS_PASSED + TESTS_FAILED))" +echo "========================================" + +if [ $TESTS_FAILED -eq 0 ]; then + print_success "All tests passed!" + exit 0 +else + print_failure "Some tests failed!" + exit 1 +fi + diff --git a/.github/workflows/test_with_docker.yaml b/.github/workflows/test_with_docker.yaml new file mode 100644 index 0000000..9c5a657 --- /dev/null +++ b/.github/workflows/test_with_docker.yaml @@ -0,0 +1,58 @@ +name: Run stretch_new_robot_install.sh in a Docker container + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + workflow_dispatch: # Allow manual triggering + +jobs: + test-docker-installation: + runs-on: ubuntu-latest + timeout-minutes: 120 # Installation can take a while + + strategy: + matrix: + fleet_id: + - stretch-se3-0000 + - stretch-re2-0001 + fail-fast: true # If False, continue testing other fleet IDs even if one fails + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Make scripts executable + run: | + chmod +x .github/docker/build.sh + chmod +x .github/docker/test.sh + + - name: Build Docker image + run: | + ./.github/docker/build.sh -f ${{ matrix.fleet_id }} -n stretch-install-test + + - name: Verify installation + run: | + ./.github/docker/test.sh -i stretch-install-test:${{ matrix.fleet_id }} + + # - name: Extract logs from container + # if: always() + # run: | + # # Create a temporary container to extract logs + # container_id=$(docker create stretch-install-test:${{ matrix.fleet_id }}) + # docker cp $container_id:/home/hello-robot/stretch_user/log /tmp/stretch-logs-${{ matrix.fleet_id }} || true + # docker rm $container_id + # + # - name: Upload extracted logs + # if: always() + # uses: actions/upload-artifact@v3 + # with: + # name: stretch-installation-logs-${{ matrix.fleet_id }} + # path: /tmp/stretch-logs-${{ matrix.fleet_id }}/ + # retention-days: 30 + # if-no-files-found: ignore + From 7363e7bfc414fc803f33692b3d83936c045358ac Mon Sep 17 00:00:00 2001 From: Shehab Attia Date: Thu, 4 Jun 2026 14:29:09 -0400 Subject: [PATCH 2/5] Resolved Dockerfile not building correctly --- .github/docker/Dockerfile | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/docker/Dockerfile b/.github/docker/Dockerfile index 3e8b130..5348829 100644 --- a/.github/docker/Dockerfile +++ b/.github/docker/Dockerfile @@ -14,9 +14,11 @@ ENV TZ=UTC # This will be used by the installation scripts ARG SETUP_FLEET_ID=stretch-se3-0000 ENV SETUP_FLEET_ID=${SETUP_FLEET_ID} +ENV USER=hello-robot # Install basic dependencies required by the installation scripts # Including iputils-ping for network connectivity checks and zip for logs packaging +# Pre-install heavy Python dependencies via apt to avoid compiling them from source RUN apt-get update && apt-get install -y \ sudo \ git \ @@ -29,6 +31,10 @@ RUN apt-get update && apt-get install -y \ expect \ zip \ software-properties-common \ + apt-utils \ + python3-numpy \ + python3-scipy \ + python3-yaml \ && rm -rf /var/lib/apt/lists/* # Pre-create necessary directories that don't exist by default in basic Docker images @@ -48,9 +54,9 @@ RUN useradd -m -s /bin/bash hello-robot && \ USER hello-robot WORKDIR /home/hello-robot -# Clone the stretch_install repository to the container +# Copy the entire stretch_install repository to the container # This must be in the home directory as expected by the scripts -RUN git clone https://github.com/hello-robot/stretch_install /home/hello-robot/stretch_install +COPY --chown=hello-robot:hello-robot . /home/hello-robot/stretch_install # Patch the stretch_initial_setup.sh script to skip git checks and network connectivity checks in Docker # This is necessary because: @@ -66,6 +72,8 @@ RUN cd /home/hello-robot/stretch_install/factory/22.04 && \ # For Docker builds, we'll create a minimal structure to satisfy the script RUN mkdir -p /home/hello-robot/${SETUP_FLEET_ID}/udev && \ echo '# Minimal udev rules for Docker build' > /home/hello-robot/${SETUP_FLEET_ID}/udev/stretch-device.rules && \ + printf 'robot:\n batch_name: NA\n serial_no: "%s"\n model_name: SE3\n tool: eoa_wrist_dw3_tool_sg3\n' "${SETUP_FLEET_ID##*-}" > /home/hello-robot/${SETUP_FLEET_ID}/stretch_configuration_params.yaml && \ + touch /home/hello-robot/${SETUP_FLEET_ID}/stretch_user_params.yaml && \ chmod -R a+r /home/hello-robot/${SETUP_FLEET_ID} # Pre-create required directories to ensure proper permissions @@ -85,7 +93,7 @@ SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] # We redirect stderr to stdout to capture all output # The script will exit with non-zero code on failure, causing the build to fail RUN cd /home/hello-robot/stretch_install && \ - bash -c 'yes | ./stretch_new_robot_install.sh -H ${SETUP_FLEET_ID} 2>&1 | tee /home/hello-robot/stretch_user/log/docker_install.log; exit ${PIPESTATUS[0]}' + bash -c 'yes | ./stretch_new_robot_install.sh -H ${SETUP_FLEET_ID} 2>&1 | tee /home/hello-robot/stretch_user/log/docker_install.log; exit ${PIPESTATUS[1]}' # Verify that the FLEET_ID was properly configured in both locations RUN if [ ! -f /etc/hello-robot/hello-robot.conf ]; then \ From 487e44bf5c89cc66f2b2dec8d76fc36f551df68d Mon Sep 17 00:00:00 2001 From: Shehab Attia Date: Thu, 4 Jun 2026 14:31:11 -0400 Subject: [PATCH 3/5] Added Dockerfiles for ubuntu 20.04 and 18.04 for RE1 and RE2. --- .github/docker/Dockerfile.18.04 | 123 ++++++++++++++++++++++++ .github/docker/Dockerfile.20.04 | 120 +++++++++++++++++++++++ .github/docker/build.sh | 9 ++ .github/workflows/test_with_docker.yaml | 34 +++---- 4 files changed, 269 insertions(+), 17 deletions(-) create mode 100644 .github/docker/Dockerfile.18.04 create mode 100644 .github/docker/Dockerfile.20.04 diff --git a/.github/docker/Dockerfile.18.04 b/.github/docker/Dockerfile.18.04 new file mode 100644 index 0000000..b615525 --- /dev/null +++ b/.github/docker/Dockerfile.18.04 @@ -0,0 +1,123 @@ +# Dockerfile for Stretch Robot Installation (Ubuntu 18.04 - RE1) +# Can be executed in GitHub Actions workflows or manually +# Usage: +# docker build -t stretch-install-18.04 -f .github/docker/Dockerfile.18.04 . +# docker run --rm stretch-install-18.04 + +FROM ubuntu:18.04 + +# Prevent interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC + +# Set the FLEET_ID environment variable +# This will be used by the installation scripts +ARG SETUP_FLEET_ID=stretch-re1-0000 +ENV SETUP_FLEET_ID=${SETUP_FLEET_ID} +ENV USER=hello-robot + +# Install basic dependencies required by the installation scripts +# Including iputils-ping for network connectivity checks and zip for logs packaging +# Pre-install heavy Python dependencies via apt to avoid compiling them from source +RUN apt-get update && apt-get install -y \ + sudo \ + git \ + curl \ + wget \ + ca-certificates \ + gnupg \ + lsb-release \ + iputils-ping \ + expect \ + zip \ + software-properties-common \ + apt-utils \ + python-numpy \ + python-scipy \ + python-yaml \ + python3-numpy \ + python3-scipy \ + python3-yaml \ + && rm -rf /var/lib/apt/lists/* + +# Pre-create necessary directories that don't exist by default in basic Docker images +# Also mock udevadm and update-initramfs which are not useful inside Docker and fail/warn in container environments +RUN mkdir -p /etc/udev/rules.d /etc/modprobe.d /etc/apt/keyrings && \ + printf '#!/bin/sh\nexit 0\n' > /usr/sbin/update-initramfs && \ + chmod +x /usr/sbin/update-initramfs && \ + printf '#!/bin/sh\nexit 0\n' > /usr/bin/udevadm && \ + chmod +x /usr/bin/udevadm + +# Create a non-root user 'hello-robot' with sudo privileges +# The installation scripts expect to run as a regular user with sudo access +RUN useradd -m -s /bin/bash hello-robot && \ + echo "hello-robot ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +# Switch to the hello-robot user +USER hello-robot +WORKDIR /home/hello-robot + +# Copy the entire stretch_install repository to the container +# This must be in the home directory as expected by the scripts +COPY --chown=hello-robot:hello-robot . /home/hello-robot/stretch_install + +# Patch the stretch_initial_setup.sh script to skip git checks and network connectivity checks in Docker +# This is necessary because: +# 1. The git remote check won't work in a Docker build context (no .git directory is copied due to .dockerignore) +# 2. Network connectivity checks (pinging google.com) may fail in some Docker build environments +RUN cd /home/hello-robot/stretch_install/factory/18.04 && \ + cp stretch_initial_setup.sh stretch_initial_setup.sh.orig && \ + sed -i '/^echo "Checking install repo is up-to-date..."/,/^fi$/d' stretch_initial_setup.sh && \ + sed -i '/^echo "Waiting to get online..."/,/^done$/c\echo "Skipping network check in Docker environment..."' stretch_initial_setup.sh + +# Create the calibration data directory structure +# The installation script expects calibration data in $HOME/ +# For Docker builds, we'll create a minimal structure to satisfy the script +RUN mkdir -p /home/hello-robot/${SETUP_FLEET_ID}/udev && \ + echo '# Minimal udev rules for Docker build' > /home/hello-robot/${SETUP_FLEET_ID}/udev/stretch-device.rules && \ + printf 'robot:\n batch_name: NA\n serial_no: "%s"\n model_name: RE1\n tool: eoa_wrist_dw3_tool_sg3\n' "${SETUP_FLEET_ID##*-}" > /home/hello-robot/${SETUP_FLEET_ID}/stretch_configuration_params.yaml && \ + touch /home/hello-robot/${SETUP_FLEET_ID}/stretch_user_params.yaml && \ + chmod -R a+r /home/hello-robot/${SETUP_FLEET_ID} + +# Pre-create required directories to ensure proper permissions +RUN mkdir -p /home/hello-robot/stretch_user/log && \ + mkdir -p /home/hello-robot/stretch_user/debug && \ + mkdir -p /home/hello-robot/stretch_user/maps && \ + mkdir -p /home/hello-robot/stretch_user/models && \ + mkdir -p /home/hello-robot/.local/bin + +# Set up error handling for the installation +# The script already uses 'set -e' and 'set -o pipefail' for error propagation +# We'll use shell options to ensure any errors cause the build to fail +SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] + +# Run the installation script with the FLEET_ID environment variable +# The -H flag passes the complete fleet ID to the script +# We redirect stderr to stdout to capture all output +# The script will exit with non-zero code on failure, causing the build to fail +RUN cd /home/hello-robot/stretch_install && \ + bash -c 'yes | ./stretch_new_robot_install.sh -H ${SETUP_FLEET_ID} 2>&1 | tee /home/hello-robot/stretch_user/log/docker_install.log; exit ${PIPESTATUS[1]}' + +# Verify that the FLEET_ID was properly configured in both locations +RUN if [ ! -f /etc/hello-robot/hello-robot.conf ]; then \ + echo "ERROR: /etc/hello-robot/hello-robot.conf not found"; \ + exit 1; \ + fi && \ + if ! grep -q "HELLO_FLEET_ID=${SETUP_FLEET_ID}" /etc/hello-robot/hello-robot.conf; then \ + echo "ERROR: FLEET_ID not properly configured in /etc/hello-robot/hello-robot.conf"; \ + cat /etc/hello-robot/hello-robot.conf; \ + exit 1; \ + fi && \ + if [ ! -d /home/hello-robot/stretch_user/${SETUP_FLEET_ID} ]; then \ + echo "ERROR: /home/hello-robot/stretch_user/${SETUP_FLEET_ID} directory not found"; \ + exit 1; \ + fi && \ + echo "SUCCESS: FLEET_ID properly configured in all required locations" + +# Set the default command to display installation success and fleet ID +CMD ["/bin/bash", "-c", "echo 'Stretch Robot Installation Complete' && \ + echo 'Fleet ID: '${SETUP_FLEET_ID} && \ + echo 'Configuration verified in:' && \ + echo ' - /etc/hello-robot/hello-robot.conf' && \ + echo ' - /home/hello-robot/stretch_user/'${SETUP_FLEET_ID} && \ + /bin/bash"] diff --git a/.github/docker/Dockerfile.20.04 b/.github/docker/Dockerfile.20.04 new file mode 100644 index 0000000..fed2c32 --- /dev/null +++ b/.github/docker/Dockerfile.20.04 @@ -0,0 +1,120 @@ +# Dockerfile for Stretch Robot Installation (Ubuntu 20.04 - RE2) +# Can be executed in GitHub Actions workflows or manually +# Usage: +# docker build -t stretch-install-20.04 -f .github/docker/Dockerfile.20.04 . +# docker run --rm stretch-install-20.04 + +FROM ubuntu:20.04 + +# Prevent interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC + +# Set the FLEET_ID environment variable +# This will be used by the installation scripts +ARG SETUP_FLEET_ID=stretch-re2-0000 +ENV SETUP_FLEET_ID=${SETUP_FLEET_ID} +ENV USER=hello-robot + +# Install basic dependencies required by the installation scripts +# Including iputils-ping for network connectivity checks and zip for logs packaging +# Pre-install heavy Python dependencies via apt to avoid compiling them from source +RUN apt-get update && apt-get install -y \ + sudo \ + git \ + curl \ + wget \ + ca-certificates \ + gnupg \ + lsb-release \ + iputils-ping \ + expect \ + zip \ + software-properties-common \ + apt-utils \ + python3-numpy \ + python3-scipy \ + python3-yaml \ + && rm -rf /var/lib/apt/lists/* + +# Pre-create necessary directories that don't exist by default in basic Docker images +# Also mock udevadm and update-initramfs which are not useful inside Docker and fail/warn in container environments +RUN mkdir -p /etc/udev/rules.d /etc/modprobe.d /etc/apt/keyrings && \ + printf '#!/bin/sh\nexit 0\n' > /usr/sbin/update-initramfs && \ + chmod +x /usr/sbin/update-initramfs && \ + printf '#!/bin/sh\nexit 0\n' > /usr/bin/udevadm && \ + chmod +x /usr/bin/udevadm + +# Create a non-root user 'hello-robot' with sudo privileges +# The installation scripts expect to run as a regular user with sudo access +RUN useradd -m -s /bin/bash hello-robot && \ + echo "hello-robot ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +# Switch to the hello-robot user +USER hello-robot +WORKDIR /home/hello-robot + +# Copy the entire stretch_install repository to the container +# This must be in the home directory as expected by the scripts +COPY --chown=hello-robot:hello-robot . /home/hello-robot/stretch_install + +# Patch the stretch_initial_setup.sh script to skip git checks and network connectivity checks in Docker +# This is necessary because: +# 1. The git remote check won't work in a Docker build context (no .git directory is copied due to .dockerignore) +# 2. Network connectivity checks (pinging google.com) may fail in some Docker build environments +RUN cd /home/hello-robot/stretch_install/factory/20.04 && \ + cp stretch_initial_setup.sh stretch_initial_setup.sh.orig && \ + sed -i '/^echo "Checking install repo is up-to-date..."/,/^fi$/d' stretch_initial_setup.sh && \ + sed -i '/^echo "Waiting to get online..."/,/^done$/c\echo "Skipping network check in Docker environment..."' stretch_initial_setup.sh + +# Create the calibration data directory structure +# The installation script expects calibration data in $HOME/ +# For Docker builds, we'll create a minimal structure to satisfy the script +RUN mkdir -p /home/hello-robot/${SETUP_FLEET_ID}/udev && \ + echo '# Minimal udev rules for Docker build' > /home/hello-robot/${SETUP_FLEET_ID}/udev/stretch-device.rules && \ + printf 'robot:\n batch_name: NA\n serial_no: "%s"\n model_name: RE2\n tool: eoa_wrist_dw3_tool_sg3\n' "${SETUP_FLEET_ID##*-}" > /home/hello-robot/${SETUP_FLEET_ID}/stretch_configuration_params.yaml && \ + touch /home/hello-robot/${SETUP_FLEET_ID}/stretch_user_params.yaml && \ + chmod -R a+r /home/hello-robot/${SETUP_FLEET_ID} + +# Pre-create required directories to ensure proper permissions +RUN mkdir -p /home/hello-robot/stretch_user/log && \ + mkdir -p /home/hello-robot/stretch_user/debug && \ + mkdir -p /home/hello-robot/stretch_user/maps && \ + mkdir -p /home/hello-robot/stretch_user/models && \ + mkdir -p /home/hello-robot/.local/bin + +# Set up error handling for the installation +# The script already uses 'set -e' and 'set -o pipefail' for error propagation +# We'll use shell options to ensure any errors cause the build to fail +SHELL ["/bin/bash", "-e", "-o", "pipefail", "-c"] + +# Run the installation script with the FLEET_ID environment variable +# The -H flag passes the complete fleet ID to the script +# We redirect stderr to stdout to capture all output +# The script will exit with non-zero code on failure, causing the build to fail +RUN cd /home/hello-robot/stretch_install && \ + bash -c 'yes | ./stretch_new_robot_install.sh -H ${SETUP_FLEET_ID} 2>&1 | tee /home/hello-robot/stretch_user/log/docker_install.log; exit ${PIPESTATUS[1]}' + +# Verify that the FLEET_ID was properly configured in both locations +RUN if [ ! -f /etc/hello-robot/hello-robot.conf ]; then \ + echo "ERROR: /etc/hello-robot/hello-robot.conf not found"; \ + exit 1; \ + fi && \ + if ! grep -q "HELLO_FLEET_ID=${SETUP_FLEET_ID}" /etc/hello-robot/hello-robot.conf; then \ + echo "ERROR: FLEET_ID not properly configured in /etc/hello-robot/hello-robot.conf"; \ + cat /etc/hello-robot/hello-robot.conf; \ + exit 1; \ + fi && \ + if [ ! -d /home/hello-robot/stretch_user/${SETUP_FLEET_ID} ]; then \ + echo "ERROR: /home/hello-robot/stretch_user/${SETUP_FLEET_ID} directory not found"; \ + exit 1; \ + fi && \ + echo "SUCCESS: FLEET_ID properly configured in all required locations" + +# Set the default command to display installation success and fleet ID +CMD ["/bin/bash", "-c", "echo 'Stretch Robot Installation Complete' && \ + echo 'Fleet ID: '${SETUP_FLEET_ID} && \ + echo 'Configuration verified in:' && \ + echo ' - /etc/hello-robot/hello-robot.conf' && \ + echo ' - /home/hello-robot/stretch_user/'${SETUP_FLEET_ID} && \ + /bin/bash"] diff --git a/.github/docker/build.sh b/.github/docker/build.sh index 4f14ff0..46fdc23 100755 --- a/.github/docker/build.sh +++ b/.github/docker/build.sh @@ -65,6 +65,15 @@ if [[ ! $FLEET_ID =~ ^stretch-(re1|re2|se3)-[0-9]{4}$ ]]; then exit 1 fi +# Determine Dockerfile based on FLEET_ID +if [[ $FLEET_ID =~ ^stretch-re1-[0-9]{4}$ ]]; then + DOCKERFILE_PATH=".github/docker/Dockerfile.18.04" +elif [[ $FLEET_ID =~ ^stretch-re2-[0-9]{4}$ ]]; then + DOCKERFILE_PATH=".github/docker/Dockerfile.20.04" +else + DOCKERFILE_PATH=".github/docker/Dockerfile" +fi + print_info "Building Stretch Robot Installation Docker image" print_info "Fleet ID: $FLEET_ID" print_info "Image name: $IMAGE_NAME" diff --git a/.github/workflows/test_with_docker.yaml b/.github/workflows/test_with_docker.yaml index 9c5a657..b79b551 100644 --- a/.github/workflows/test_with_docker.yaml +++ b/.github/workflows/test_with_docker.yaml @@ -17,6 +17,7 @@ jobs: fleet_id: - stretch-se3-0000 - stretch-re2-0001 + - stretch-re1-0000 fail-fast: true # If False, continue testing other fleet IDs even if one fails steps: @@ -39,20 +40,19 @@ jobs: run: | ./.github/docker/test.sh -i stretch-install-test:${{ matrix.fleet_id }} - # - name: Extract logs from container - # if: always() - # run: | - # # Create a temporary container to extract logs - # container_id=$(docker create stretch-install-test:${{ matrix.fleet_id }}) - # docker cp $container_id:/home/hello-robot/stretch_user/log /tmp/stretch-logs-${{ matrix.fleet_id }} || true - # docker rm $container_id - # - # - name: Upload extracted logs - # if: always() - # uses: actions/upload-artifact@v3 - # with: - # name: stretch-installation-logs-${{ matrix.fleet_id }} - # path: /tmp/stretch-logs-${{ matrix.fleet_id }}/ - # retention-days: 30 - # if-no-files-found: ignore - + - name: Extract logs from container + if: always() + run: | + # Create a temporary container to extract logs + container_id=$(docker create stretch-install-test:${{ matrix.fleet_id }}) + docker cp $container_id:/home/hello-robot/stretch_user/log /tmp/stretch-logs-${{ matrix.fleet_id }} || true + docker rm $container_id + + - name: Upload extracted logs + if: always() + uses: actions/upload-artifact@v3 + with: + name: stretch-installation-logs-${{ matrix.fleet_id }} + path: /tmp/stretch-logs-${{ matrix.fleet_id }}/ + retention-days: 30 + if-no-files-found: ignore From c2cfbbb1af3388a0a7cf53d600114dda2bd65bce Mon Sep 17 00:00:00 2001 From: Shehab Attia Date: Thu, 4 Jun 2026 15:12:13 -0400 Subject: [PATCH 4/5] Runs all the tests now --- .github/docker/test.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/docker/test.sh b/.github/docker/test.sh index b0f547e..862b6e3 100755 --- a/.github/docker/test.sh +++ b/.github/docker/test.sh @@ -2,8 +2,6 @@ # Test script for Stretch Robot Installation Docker image # This script verifies that the Docker installation completed successfully -set -e - # Default values IMAGE_NAME="${IMAGE_NAME:-stretch-install:latest}" From cc4eaf3a260df0b8a96476434b3531833c32c705 Mon Sep 17 00:00:00 2001 From: Shehab Attia Date: Thu, 4 Jun 2026 15:17:12 -0400 Subject: [PATCH 5/5] Removed upload artifacts --- .github/workflows/test_with_docker.yaml | 30 ++++++++++++------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/test_with_docker.yaml b/.github/workflows/test_with_docker.yaml index b79b551..536ae1e 100644 --- a/.github/workflows/test_with_docker.yaml +++ b/.github/workflows/test_with_docker.yaml @@ -40,19 +40,19 @@ jobs: run: | ./.github/docker/test.sh -i stretch-install-test:${{ matrix.fleet_id }} - - name: Extract logs from container - if: always() - run: | - # Create a temporary container to extract logs - container_id=$(docker create stretch-install-test:${{ matrix.fleet_id }}) - docker cp $container_id:/home/hello-robot/stretch_user/log /tmp/stretch-logs-${{ matrix.fleet_id }} || true - docker rm $container_id + # - name: Extract logs from container + # if: always() + # run: | + # # Create a temporary container to extract logs + # container_id=$(docker create stretch-install-test:${{ matrix.fleet_id }}) + # docker cp $container_id:/home/hello-robot/stretch_user/log /tmp/stretch-logs-${{ matrix.fleet_id }} || true + # docker rm $container_id - - name: Upload extracted logs - if: always() - uses: actions/upload-artifact@v3 - with: - name: stretch-installation-logs-${{ matrix.fleet_id }} - path: /tmp/stretch-logs-${{ matrix.fleet_id }}/ - retention-days: 30 - if-no-files-found: ignore + # - name: Upload extracted logs + # if: always() + # uses: actions/upload-artifact@v3 + # with: + # name: stretch-installation-logs-${{ matrix.fleet_id }} + # path: /tmp/stretch-logs-${{ matrix.fleet_id }}/ + # retention-days: 30 + # if-no-files-found: ignore