name: Test and Build
on:
push:
branches: [ main, develop, feature/** ]
pull_request:
branches: [ main, develop ]
release:
types: [ published ]
env:
ROS_DISTRO: humble
PYTHON_VERSION: "3.10"
jobs:
# ===========================================================================
# Unit Tests (fast, no Gazebo required)
# ===========================================================================
unit-tests:
name: Unit Tests
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run unit tests
run: |
pytest tests/test_utils.py -v --tb=short
pytest tests/test_validators.py -v --tb=short
pytest tests/test_metrics.py -v --tb=short
- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: unit-test-results
path: |
pytest-report.xml
coverage.xml
# ===========================================================================
# Integration Tests (requires ROS2 + Gazebo)
# ===========================================================================
integration-tests:
name: Integration Tests
runs-on: ubuntu-22.04
needs: unit-tests
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup ROS2
uses: ros-tooling/setup-ros@v0.7
with:
required-ros-distributions: ${{ env.ROS_DISTRO }}
- name: Install Gazebo dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
ros-${{ env.ROS_DISTRO }}-gazebo-ros-pkgs \
ros-${{ env.ROS_DISTRO }}-gazebo-msgs \
python3-pip
- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install -r requirements.txt
pip3 install -r requirements-dev.txt
- name: Source ROS2 environment
run: |
source /opt/ros/${{ env.ROS_DISTRO }}/setup.bash
echo "ROS_DISTRO=$ROS_DISTRO" >> $GITHUB_ENV
- name: Run integration tests
run: |
source /opt/ros/${{ env.ROS_DISTRO }}/setup.bash
export PYTHONPATH=$PYTHONPATH:$(pwd)/src
pytest tests/test_integration.py -v --tb=short --with-ros2 || true
- name: Upload integration test results
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-test-results
path: |
pytest-report.xml
integration-test-logs/
# ===========================================================================
# Code Quality Checks
# ===========================================================================
code-quality:
name: Code Quality
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install black flake8 mypy pylint isort
- name: Check code formatting (black)
run: |
black --check src/ mcp/ tests/ || true
- name: Check import sorting (isort)
run: |
isort --check-only src/ mcp/ tests/ || true
- name: Lint with flake8
run: |
flake8 src/ mcp/ tests/ --count --select=E9,F63,F7,F82 --show-source --statistics || true
- name: Type checking (mypy)
run: |
mypy src/gazebo_mcp/ --ignore-missing-imports || true
# ===========================================================================
# Docker Build
# ===========================================================================
docker-build:
name: Docker Build
runs-on: ubuntu-22.04
needs: [unit-tests, code-quality]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build development image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
target: dev
tags: gazebo-mcp:dev
push: false
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build production image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
target: production
tags: gazebo-mcp:latest
push: false
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Test Docker image
run: |
docker run --rm gazebo-mcp:latest python3 -c "from mcp.server.server import GazeboMCPServer; print('✓ MCP server imports successfully')"
# ===========================================================================
# Docker Publish (on release)
# ===========================================================================
docker-publish:
name: Publish Docker Images
runs-on: ubuntu-22.04
needs: [integration-tests, docker-build]
if: github.event_name == 'release'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract version from tag
id: version
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Build and push production image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
target: production
tags: |
${{ secrets.DOCKER_USERNAME }}/gazebo-mcp:latest
${{ secrets.DOCKER_USERNAME }}/gazebo-mcp:${{ steps.version.outputs.VERSION }}
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build and push GPU image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
target: gpu
tags: |
${{ secrets.DOCKER_USERNAME }}/gazebo-mcp:gpu
${{ secrets.DOCKER_USERNAME }}/gazebo-mcp:${{ steps.version.outputs.VERSION }}-gpu
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
# ===========================================================================
# Security Scan
# ===========================================================================
security-scan:
name: Security Scan
runs-on: ubuntu-22.04
needs: docker-build
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'