Skip to content
Latchkey

Build and Push Docker Images workflow (huggingface/OpenEnv)

The Build and Push Docker Images workflow from huggingface/OpenEnv, explained and optimized by Latchkey.

C

CI health: C - fair

The optimized version below adds run de-duplication, job timeouts, SHA-pinned actions.

Source: huggingface/OpenEnv.github/workflows/docker-build.ymlLicense BSD-3-ClauseView source

What it does

This is the Build and Push Docker Images workflow from the huggingface/OpenEnv repository, a real project running GitHub Actions. It is shown here with attribution under its BSD-3-Clause license.

Below, Latchkey shows a faster, safer version produced by its optimization engine.

The workflow

workflow (.yml)
name: Build and Push Docker Images

on:
  push:
    branches:
      - main
    paths:
      - 'envs/**/Dockerfile'
      - 'envs/**/*.py'
      - '.github/workflows/docker-build.yml'
  workflow_dispatch:  # Allow manual trigger

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@v7

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Log in to GHCR
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata for base image
        id: meta
        uses: docker/metadata-action@v6
        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@v7
        with:
          context: .
          file: src/openenv/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 environment images (depends on base)
  build-envs:
    runs-on: ubuntu-latest
    needs: build-base  # Wait for base image to be built
    permissions:
      contents: read
      packages: write

    strategy:
      fail-fast: false
      matrix:
        image:
          - name: echo-env
            dockerfile: envs/echo_env/server/Dockerfile
            context: envs/echo_env
          - name: chat-env
            dockerfile: envs/chat_env/server/Dockerfile
            context: envs/chat_env
          - name: coding-env
            dockerfile: envs/coding_env/server/Dockerfile
          - name: sumo-rl-env
            dockerfile: envs/sumo_rl_env/server/Dockerfile
            context: envs/sumo_rl_env
          - name: atari-env
            dockerfile: envs/atari_env/server/Dockerfile
            context: envs/atari_env
          - name: git-env
            dockerfile: envs/git_env/server/Dockerfile
            context: envs/git_env
          - name: connect4_env
            dockerfile: envs/connect4_env/server/Dockerfile
            context: envs/connect4_env
          - name: chess-env
            dockerfile: envs/chess_env/server/Dockerfile
            context: envs/chess_env
          - name: tbench2-env
            dockerfile: envs/tbench2_env/server/Dockerfile
            context: envs/tbench2_env
          - name: textarena-env
            dockerfile: envs/textarena_env/server/Dockerfile
            context: envs/textarena_env
          - name: browsergym-env
            dockerfile: envs/browsergym_env/server/Dockerfile
            context: envs/browsergym_env
          - name: snake-env
            dockerfile: envs/snake_env/server/Dockerfile
            context: envs/snake_env
          - name: unity-env
            dockerfile: envs/unity_env/server/Dockerfile
            context: envs/unity_env
          - name: opencode-env
            dockerfile: envs/opencode_env/server/Dockerfile
            context: envs/opencode_env
          - name: openapp-env
            dockerfile: envs/openapp_env/server/Dockerfile
            context: envs/openapp_env
          - name: maze-env
            dockerfile: envs/maze_env/server/Dockerfile
            context: envs/maze_env
          - name: openspiel-env
            dockerfile: envs/openspiel_env/server/Dockerfile
            context: envs/openspiel_env

    steps:
      - name: Checkout code
        uses: actions/checkout@v7

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Log in to GHCR
        uses: docker/login-action@v4
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata
        id: meta
        uses: docker/metadata-action@v6
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.image.name }}
          tags: |
            type=raw,value=latest,enable={{is_default_branch}}
            type=sha

      - name: Build and push environment image
        uses: docker/build-push-action@v7
        with:
          context: ${{ matrix.image.context || '.' }}
          file: ${{ matrix.image.dockerfile }}
          push: true
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha,scope=${{ matrix.image.name }}
          cache-to: type=gha,mode=max,scope=${{ matrix.image.name }}
          build-args: |
            BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-base:latest

The same workflow, on Latchkey

Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.

name: Build and Push Docker Images on:  push:    branches:      - main    paths:      - 'envs/**/Dockerfile'      - 'envs/**/*.py'      - '.github/workflows/docker-build.yml'  workflow_dispatch:  # Allow manual trigger env:  REGISTRY: ghcr.io  IMAGE_PREFIX: ${{ github.repository_owner }}/openenv concurrency:  group: ${{ github.workflow }}-${{ github.ref }}  cancel-in-progress: true jobs:  # Job 1: Build base image first  build-base:    timeout-minutes: 30    runs-on: latchkey-small    permissions:      contents: read      packages: write     steps:      - name: Checkout code        uses: actions/checkout@v7       - name: Set up Docker Buildx        uses: docker/setup-buildx-action@v4       - name: Log in to GHCR        uses: docker/login-action@v4        with:          registry: ${{ env.REGISTRY }}          username: ${{ github.actor }}          password: ${{ secrets.GITHUB_TOKEN }}       - name: Extract metadata for base image        id: meta        uses: docker/metadata-action@v6        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@v7        with:          context: .          file: src/openenv/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 environment images (depends on base)  build-envs:    timeout-minutes: 30    runs-on: latchkey-small    needs: build-base  # Wait for base image to be built    permissions:      contents: read      packages: write     strategy:      fail-fast: false      matrix:        image:          - name: echo-env            dockerfile: envs/echo_env/server/Dockerfile            context: envs/echo_env          - name: chat-env            dockerfile: envs/chat_env/server/Dockerfile            context: envs/chat_env          - name: coding-env            dockerfile: envs/coding_env/server/Dockerfile          - name: sumo-rl-env            dockerfile: envs/sumo_rl_env/server/Dockerfile            context: envs/sumo_rl_env          - name: atari-env            dockerfile: envs/atari_env/server/Dockerfile            context: envs/atari_env          - name: git-env            dockerfile: envs/git_env/server/Dockerfile            context: envs/git_env          - name: connect4_env            dockerfile: envs/connect4_env/server/Dockerfile            context: envs/connect4_env          - name: chess-env            dockerfile: envs/chess_env/server/Dockerfile            context: envs/chess_env          - name: tbench2-env            dockerfile: envs/tbench2_env/server/Dockerfile            context: envs/tbench2_env          - name: textarena-env            dockerfile: envs/textarena_env/server/Dockerfile            context: envs/textarena_env          - name: browsergym-env            dockerfile: envs/browsergym_env/server/Dockerfile            context: envs/browsergym_env          - name: snake-env            dockerfile: envs/snake_env/server/Dockerfile            context: envs/snake_env          - name: unity-env            dockerfile: envs/unity_env/server/Dockerfile            context: envs/unity_env          - name: opencode-env            dockerfile: envs/opencode_env/server/Dockerfile            context: envs/opencode_env          - name: openapp-env            dockerfile: envs/openapp_env/server/Dockerfile            context: envs/openapp_env          - name: maze-env            dockerfile: envs/maze_env/server/Dockerfile            context: envs/maze_env          - name: openspiel-env            dockerfile: envs/openspiel_env/server/Dockerfile            context: envs/openspiel_env     steps:      - name: Checkout code        uses: actions/checkout@v7       - name: Set up Docker Buildx        uses: docker/setup-buildx-action@v4       - name: Log in to GHCR        uses: docker/login-action@v4        with:          registry: ${{ env.REGISTRY }}          username: ${{ github.actor }}          password: ${{ secrets.GITHUB_TOKEN }}       - name: Extract metadata        id: meta        uses: docker/metadata-action@v6        with:          images: ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.image.name }}          tags: |            type=raw,value=latest,enable={{is_default_branch}}            type=sha       - name: Build and push environment image        uses: docker/build-push-action@v7        with:          context: ${{ matrix.image.context || '.' }}          file: ${{ matrix.image.dockerfile }}          push: true          platforms: linux/amd64,linux/arm64          tags: ${{ steps.meta.outputs.tags }}          labels: ${{ steps.meta.outputs.labels }}          cache-from: type=gha,scope=${{ matrix.image.name }}          cache-to: type=gha,mode=max,scope=${{ matrix.image.name }}          build-args: |            BASE_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-base:latest 

What changed

  • Run on Latchkey managed runners with one line (runs-on), which apply the fixes below automatically and self-heal transient failures. This example uses latchkey-small; pick the runner size that fits the job.
  • Cancel superseded runs when a branch or PR gets a newer push.
  • Add a job timeout so a hung step cannot burn hours of runner time.

4 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.

What Latchkey heals here

This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:

  • Container pulls and builds

This workflow runs 2 jobs (18 with the matrix expanded) per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.

Actions used in this workflow

Frequently asked questions

What does the Build and Push Docker Images workflow (huggingface/OpenEnv) workflow do?
This is the Build and Push Docker Images workflow from the huggingface/OpenEnv repository, a real project running GitHub Actions. It is shown here with attribution under its BSD-3-Clause license.
What CI health grade does this workflow get?
This Docker workflow grades C. Paste your own workflow into the Latchkey grader to see its grade and the exact fixes.
How can I improve this Docker workflow?
Apply run de-duplication, job timeouts, SHA-pinned actions. Latchkey applies these automatically on managed runners when you point runs-on at Latchkey.

References