From a85e20f307c965380c380876c6ef8d7639d5f22e Mon Sep 17 00:00:00 2001 From: Zach Wentz Date: Tue, 21 Oct 2025 11:51:27 -0400 Subject: [PATCH 1/2] Fix OpenSpiel docker builds --- .github/workflows/deploy-hf-env.yml | 2 +- .github/workflows/openspiel_base_build.yml | 106 ++++++++++++++++ scripts/prepare_hf_deployment.sh | 117 +++++++++--------- src/envs/openspiel_env/server/Dockerfile | 64 ++-------- .../server/Dockerfile.openspiel-base | 65 ++++++++++ 5 files changed, 237 insertions(+), 117 deletions(-) create mode 100644 .github/workflows/openspiel_base_build.yml create mode 100644 src/envs/openspiel_env/server/Dockerfile.openspiel-base diff --git a/.github/workflows/deploy-hf-env.yml b/.github/workflows/deploy-hf-env.yml index db200d72..fdf505c5 100644 --- a/.github/workflows/deploy-hf-env.yml +++ b/.github/workflows/deploy-hf-env.yml @@ -167,7 +167,7 @@ jobs: # Cleanup cd .. rm -rf hf-space - rm -rf hf-staging_$ENV_NAME_$ENV_NAME + rm -rf hf-staging_$ENV_NAME # Job to deploy single environment deploy-single: diff --git a/.github/workflows/openspiel_base_build.yml b/.github/workflows/openspiel_base_build.yml new file mode 100644 index 00000000..afe6be00 --- /dev/null +++ b/.github/workflows/openspiel_base_build.yml @@ -0,0 +1,106 @@ +name: Build OpenSpiel Base Image + +on: + workflow_dispatch: # Manual trigger only + inputs: + force_rebuild: + description: 'Force rebuild even if base image exists' + required: false + default: 'false' + type: boolean + +env: + REGISTRY: ghcr.io + IMAGE_PREFIX: ${{ github.repository_owner }}/openenv + +jobs: + # Job 1: Build base image first + build-base: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for base image + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-base + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=sha + + - name: Build and push base image + uses: docker/build-push-action@v5 + with: + context: . + file: src/core/containers/images/Dockerfile + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=base + cache-to: type=gha,mode=max,scope=base + + # Job 2: Build OpenSpiel base image (depends on base) + build-openspiel-base: + runs-on: 8-core-ubuntu + needs: build-base # Wait for base image to be built + permissions: + contents: read + packages: write + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata for OpenSpiel base image + id: meta-openspiel-base + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-openspiel-base + tags: | + type=raw,value=latest,enable={{is_default_branch}} + type=sha + + - name: Build and push OpenSpiel base image + uses: docker/build-push-action@v5 + with: + context: . + file: src/envs/openspiel_env/server/Dockerfile.openspiel-base + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta-openspiel-base.outputs.tags }} + labels: ${{ steps.meta-openspiel-base.outputs.labels }} + cache-from: type=gha,scope=openspiel-base + cache-to: type=gha,mode=max,scope=openspiel-base + + - name: Build summary + run: | + echo "✅ OpenSpiel base image built and pushed successfully!" + echo "đŸ“Ļ Image: ${{ steps.meta-openspiel-base.outputs.tags }}" + echo "🚀 Next regular build will use this new base image" diff --git a/scripts/prepare_hf_deployment.sh b/scripts/prepare_hf_deployment.sh index 9cbf44c8..75451b6a 100755 --- a/scripts/prepare_hf_deployment.sh +++ b/scripts/prepare_hf_deployment.sh @@ -94,63 +94,23 @@ DOCKERFILE_EOF "openspiel_env") # OpenSpiel requires special C++ build process - replace entire Dockerfile cat > $CURRENT_STAGING_DIR/Dockerfile << DOCKERFILE_EOF -# OpenSpiel requires complex C++ build - using special multi-stage approach -# Stage 1: Build OpenSpiel C++ bindings -FROM python:3.11 AS openspiel-builder - -# Avoid interactive prompts during build -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=UTC - -# Install build dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - build-essential \ - clang \ - cmake \ - curl \ - git \ - sudo \ - && rm -rf /var/lib/apt/lists/* - -# Set up OpenSpiel build directory -RUN mkdir /repo -WORKDIR /repo - -# Clone OpenSpiel -RUN git clone https://github.com/google-deepmind/open_spiel.git . - -# Run OpenSpiel's installation script (downloads C++ dependencies) -RUN ./install.sh - -# Install Python dependencies -RUN pip3 install --no-cache-dir --upgrade setuptools testresources importlib_metadata -RUN pip3 install --no-cache-dir --upgrade -r requirements.txt cmake - -# Build OpenSpiel with Python 3.11 -RUN mkdir -p build -WORKDIR /repo/build -RUN cmake -DPython3_EXECUTABLE=$(which python3) -DCMAKE_CXX_COMPILER=$(which clang++) ../open_spiel -RUN make -j$(nproc) pyspiel - -# Stage 2: Use the specified openenv-base image -FROM $BASE_IMAGE_REF - -# Copy OpenSpiel build artifacts from builder -RUN mkdir -p /repo -COPY --from=openspiel-builder /repo /repo - -# Install OpenSpiel Python requirements in runtime -WORKDIR /repo -RUN pip3 install --no-cache-dir --upgrade -r requirements.txt - -# Set Python path for OpenSpiel -ENV PYTHONPATH=/repo:/repo/build/python:${PYTHONPATH} - -# Copy OpenEnv core +# OpenSpiel environment using pre-built OpenSpiel base image +# Use the pre-built OpenSpiel base image (contains compiled OpenSpiel) +# Built from: docker build -t openspiel-base:latest -f src/envs/openspiel_env/server/Dockerfile.openspiel-base . +# In GitHub Actions, this is overridden to use the GHCR base image +ARG OPENSPIEL_BASE_IMAGE=openspiel-base:latest +FROM \${OPENSPIEL_BASE_IMAGE} + +# Copy OpenEnv core (base image already set WORKDIR=/app) WORKDIR /app COPY src/core/ /app/src/core/ + +# Copy OpenSpiel environment COPY src/envs/openspiel_env/ /app/src/envs/openspiel_env/ +# Copy README for web interface documentation +COPY src/envs/openspiel_env/README.md /app/README.md + # Extend Python path for OpenEnv (base image set PYTHONPATH=/app/src) # We prepend OpenSpiel paths ENV PYTHONPATH=/repo:/repo/build/python:/app/src @@ -160,11 +120,13 @@ ENV OPENSPIEL_GAME=catch ENV OPENSPIEL_AGENT_PLAYER=0 ENV OPENSPIEL_OPPONENT_POLICY=random -# Health check +# Health check (curl is provided by openenv-base) HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 -# Run the FastAPI server +# Note: EXPOSE 8000 already set by openenv-base + +# Run the FastAPI server (uvicorn installed by openenv-base) CMD ["uvicorn", "envs.openspiel_env.server.app:app", "--host", "0.0.0.0", "--port", "8000"] DOCKERFILE_EOF echo "Created special OpenSpiel Dockerfile with C++ build process" @@ -205,12 +167,46 @@ create_readme() { # Capitalize first letter of environment name env_title=$(echo "$env_name" | awk '{print toupper(substr($0,1,1)) substr($0,2)}') + # Set environment-specific colors and emoji + case $env_name in + "atari_env") + EMOJI="đŸ•šī¸" + COLOR_FROM="#FF6200" + COLOR_TO="#D4151B" + ;; + "coding_env") + EMOJI="đŸ’ģ" + COLOR_FROM="#007ACC" + COLOR_TO="#1E1E1E" + ;; + "openspiel_env") + EMOJI="🎮" + COLOR_FROM="#9146FF" + COLOR_TO="#00FFA3" + ;; + "echo_env") + EMOJI="🔊" + COLOR_FROM="#00C9FF" + COLOR_TO="#1B2845" + ;; + "chat_env") + EMOJI="đŸ’Ŧ" + COLOR_FROM="#0084FF" + COLOR_TO="#25D366" + ;; + *) + EMOJI="đŸŗ" + COLOR_FROM="blue" + COLOR_TO="green" + ;; + esac + cat > $CURRENT_STAGING_DIR/README.md << README_EOF --- title: ${env_title} Environment Server -emoji: đŸŗ -colorFrom: blue -colorTo: green +emoji: ${EMOJI} +colorFrom: ${COLOR_FROM} +colorTo: ${COLOR_TO} sdk: docker pinned: false app_port: 8000 @@ -310,9 +306,10 @@ Provides access to OpenSpiel games for multi-agent reinforcement learning. Send a POST request to `/step` with: ```json { - "action": 0 + "action": { + "action_id": 1 + } } -``` README_EOF ;; esac diff --git a/src/envs/openspiel_env/server/Dockerfile b/src/envs/openspiel_env/server/Dockerfile index 47421f70..30e00b38 100644 --- a/src/envs/openspiel_env/server/Dockerfile +++ b/src/envs/openspiel_env/server/Dockerfile @@ -4,60 +4,12 @@ # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. -# Multi-stage build for OpenSpiel + OpenEnv -# Stage 1: Build OpenSpiel C++ bindings -# Using Python 3.11 to match envtorch-base -FROM python:3.11 AS openspiel-builder +# Use the pre-built OpenSpiel base image +# Built from: docker build -t openspiel-base:latest -f src/envs/openspiel_env/server/Dockerfile.openspiel-base . +# In GitHub Actions, this is overridden to use the GHCR base image -# Avoid interactive prompts during build -ENV DEBIAN_FRONTEND=noninteractive -ENV TZ=UTC - -# Install build dependencies -RUN apt-get update && apt-get install -y --no-install-recommends \ - build-essential \ - clang \ - cmake \ - curl \ - git \ - sudo \ - && rm -rf /var/lib/apt/lists/* - -# Set up OpenSpiel build directory -RUN mkdir /repo -WORKDIR /repo - -# Clone OpenSpiel -RUN git clone https://github.com/google-deepmind/open_spiel.git . - -# Run OpenSpiel's installation script (downloads C++ dependencies) -RUN ./install.sh - -# Install Python dependencies -RUN pip3 install --no-cache-dir --upgrade setuptools testresources importlib_metadata -RUN pip3 install --no-cache-dir --upgrade -r requirements.txt cmake - -# Build OpenSpiel with Python 3.11 -RUN mkdir -p build -WORKDIR /repo/build -RUN cmake -DPython3_EXECUTABLE=$(which python3) -DCMAKE_CXX_COMPILER=$(which clang++) ../open_spiel -RUN make -j$(nproc) pyspiel - -# Stage 2: Runtime image using published openenv-base -# Uses the standardized base image from GitHub Container Registry -# See: https://github.com/meta-pytorch/OpenEnv/pkgs/container/openenv-base -FROM ghcr.io/meta-pytorch/openenv-base:latest - -# Copy OpenSpiel build artifacts from builder -RUN mkdir -p /repo -COPY --from=openspiel-builder /repo /repo - -# Install OpenSpiel Python requirements in runtime -WORKDIR /repo -RUN pip3 install --no-cache-dir --upgrade -r requirements.txt - -# Set Python path for OpenSpiel -ENV PYTHONPATH=/repo:/repo/build/python:${PYTHONPATH} +ARG OPENSPIEL_BASE_IMAGE=openspiel-base:latest +FROM ${OPENSPIEL_BASE_IMAGE} # Copy OpenEnv core (base image already set WORKDIR=/app) WORKDIR /app @@ -78,11 +30,11 @@ ENV OPENSPIEL_GAME=catch ENV OPENSPIEL_AGENT_PLAYER=0 ENV OPENSPIEL_OPPONENT_POLICY=random -# Health check (curl is provided by envtorch-base) +# Health check (curl is provided by openenv-base) HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8000/health || exit 1 -# Note: EXPOSE 8000 already set by envtorch-base +# Note: EXPOSE 8000 already set by openenv-base -# Run the FastAPI server (uvicorn installed by envtorch-base) +# Run the FastAPI server (uvicorn installed by openenv-base) CMD ["uvicorn", "envs.openspiel_env.server.app:app", "--host", "0.0.0.0", "--port", "8000"] diff --git a/src/envs/openspiel_env/server/Dockerfile.openspiel-base b/src/envs/openspiel_env/server/Dockerfile.openspiel-base new file mode 100644 index 00000000..a532b5e3 --- /dev/null +++ b/src/envs/openspiel_env/server/Dockerfile.openspiel-base @@ -0,0 +1,65 @@ +# Copyright (c) Meta Platforms, Inc. and affiliates. +# All rights reserved. +# +# This source code is licensed under the BSD-style license found in the +# LICENSE file in the root directory of this source tree. + +# Pre-built OpenSpiel base image +# This image contains OpenSpiel compiled and ready to use +# Built from: docker build -t openspiel-base:latest -f src/envs/openspiel_env/server/Dockerfile.openspiel-base . +# In GitHub Actions, this is overridden to use the GHCR base image +ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest +FROM ${BASE_IMAGE} + +# Avoid interactive prompts during build +ENV DEBIAN_FRONTEND=noninteractive +ENV TZ=UTC + +# Install build dependencies (curl already installed by openenv-base) +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + clang \ + cmake \ + git \ + sudo \ + && rm -rf /var/lib/apt/lists/* + +# Set up OpenSpiel build directory +RUN mkdir /repo +WORKDIR /repo + +# Clone OpenSpiel +RUN git clone https://github.com/google-deepmind/open_spiel.git . + +# Run OpenSpiel's installation script (downloads C++ dependencies) +RUN ./install.sh + +# Install Python dependencies +RUN pip3 install --no-cache-dir --upgrade setuptools testresources importlib_metadata +RUN pip3 install --no-cache-dir --upgrade -r requirements.txt cmake + +# Build OpenSpiel with Python 3.11 +# Use the exact same Python executable as the base image +RUN mkdir -p build +WORKDIR /repo/build +RUN cmake -DPython3_EXECUTABLE=/usr/local/bin/python3 -DCMAKE_CXX_COMPILER=$(which clang++) ../open_spiel +RUN make -j$(nproc) pyspiel + +# Install OpenSpiel Python requirements +WORKDIR /repo +RUN pip3 install --no-cache-dir --upgrade -r requirements.txt + +# Set Python path for OpenSpiel +ENV PYTHONPATH=/repo:/repo/build/python:${PYTHONPATH} + +# Test OpenSpiel import to verify ABI compatibility +RUN python3 -c "import pyspiel; print('OpenSpiel import successful')" || echo "OpenSpiel import failed" + +# Clean up build dependencies to reduce image size +RUN apt-get remove -y build-essential clang cmake git sudo || true && \ + apt-get autoremove -y && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Set working directory back to /app (standard for openenv-base) +WORKDIR /app From 84eac1b9fb9929104305357a5062f59c3f6b07c1 Mon Sep 17 00:00:00 2001 From: Zach Wentz Date: Tue, 21 Oct 2025 21:15:59 -0400 Subject: [PATCH 2/2] Cleanup local testing of docker builds --- scripts/prepare_hf_deployment.sh | 5 +---- src/envs/openspiel_env/server/Dockerfile | 1 - src/envs/openspiel_env/server/Dockerfile.openspiel-base | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/scripts/prepare_hf_deployment.sh b/scripts/prepare_hf_deployment.sh index 75451b6a..b0793222 100755 --- a/scripts/prepare_hf_deployment.sh +++ b/scripts/prepare_hf_deployment.sh @@ -95,10 +95,7 @@ DOCKERFILE_EOF # OpenSpiel requires special C++ build process - replace entire Dockerfile cat > $CURRENT_STAGING_DIR/Dockerfile << DOCKERFILE_EOF # OpenSpiel environment using pre-built OpenSpiel base image -# Use the pre-built OpenSpiel base image (contains compiled OpenSpiel) -# Built from: docker build -t openspiel-base:latest -f src/envs/openspiel_env/server/Dockerfile.openspiel-base . -# In GitHub Actions, this is overridden to use the GHCR base image -ARG OPENSPIEL_BASE_IMAGE=openspiel-base:latest +ARG OPENSPIEL_BASE_IMAGE=ghcr.io/meta-pytorch/openenv-openspiel-base:sha-e622c7e FROM \${OPENSPIEL_BASE_IMAGE} # Copy OpenEnv core (base image already set WORKDIR=/app) diff --git a/src/envs/openspiel_env/server/Dockerfile b/src/envs/openspiel_env/server/Dockerfile index 30e00b38..48ccff33 100644 --- a/src/envs/openspiel_env/server/Dockerfile +++ b/src/envs/openspiel_env/server/Dockerfile @@ -7,7 +7,6 @@ # Use the pre-built OpenSpiel base image # Built from: docker build -t openspiel-base:latest -f src/envs/openspiel_env/server/Dockerfile.openspiel-base . # In GitHub Actions, this is overridden to use the GHCR base image - ARG OPENSPIEL_BASE_IMAGE=openspiel-base:latest FROM ${OPENSPIEL_BASE_IMAGE} diff --git a/src/envs/openspiel_env/server/Dockerfile.openspiel-base b/src/envs/openspiel_env/server/Dockerfile.openspiel-base index a532b5e3..284bfaee 100644 --- a/src/envs/openspiel_env/server/Dockerfile.openspiel-base +++ b/src/envs/openspiel_env/server/Dockerfile.openspiel-base @@ -8,7 +8,7 @@ # This image contains OpenSpiel compiled and ready to use # Built from: docker build -t openspiel-base:latest -f src/envs/openspiel_env/server/Dockerfile.openspiel-base . # In GitHub Actions, this is overridden to use the GHCR base image -ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest +ARG BASE_IMAGE=openenv-base:latest FROM ${BASE_IMAGE} # Avoid interactive prompts during build