Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Copy this file to .env and customize as needed

# Gitea Service Configuration
GITEA_URL=http://host.docker.internal:3000
GITEA_USERNAME=gitea
GITEA_PASSWORD=gitea123
2 changes: 2 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ jobs:
dockerfile: src/envs/sumo_rl_env/server/Dockerfile
- name: atari-env
dockerfile: src/envs/atari_env/server/Dockerfile
- name: git-env
dockerfile: src/envs/git_env/server/Dockerfile

steps:
- name: Checkout code
Expand Down
142 changes: 142 additions & 0 deletions examples/local_git_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#!/usr/bin/env python3
"""
Simple test showing how users will use GitEnv.from_docker_image().

This is the simplest possible usage.

Prerequisites:
1. .env file configured (copy from .env.example)
2. Shared Gitea running: ./scripts/setup_shared_gitea.sh
3. OpenEnv repo migrated to Gitea (see README)
"""

import os
import sys
from pathlib import Path

# Load environment variables from .env file
from dotenv import load_dotenv
load_dotenv()

# Add src to path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

from envs.git_env import GitAction, GitEnv


def main():
"""Test GitEnv.from_docker_image()."""
print("=" * 60)
print("GitEnv.from_docker_image() Test")
print("=" * 60)
print()

try:
# Pass environment variables from .env to container
env_vars = {
"GITEA_URL": os.getenv("GITEA_URL"),
"GITEA_USERNAME": os.getenv("GITEA_USERNAME"),
"GITEA_PASSWORD": os.getenv("GITEA_PASSWORD"),
}

# Verify env vars are loaded
if not all(env_vars.values()):
print("❌ Error: Required environment variables not found in .env")
print(" Make sure .env file exists (copy from .env.example)")
return False

print("Creating client from Docker image with .env credentials...")
print(" Using GitEnv.from_docker_image() factory method")
print()

# Create client using from_docker_image factory method
client = GitEnv.from_docker_image("git-env:latest", env_vars=env_vars)

print("✓ Client created and container started!\n")

# Now use it like any other client
print("Testing the environment:")
print("-" * 60)

# Reset
print("\n1. Reset:")
result = client.reset()
print(f" Message: {result.observation.message}")
print(f" Success: {result.observation.success}")

# Get initial state
state = client.state()
print(f" State: episode_id={state.episode_id}, step_count={state.step_count}")
print(f" Gitea ready: {state.gitea_ready}")

# List repositories
print("\n2. List repositories:")
result = client.step(GitAction(action_type="list_repos"))
print(f" Success: {result.observation.success}")
print(f" Found {len(result.observation.repos)} repositories")
for repo in result.observation.repos:
print(f" - {repo['name']}")

# Clone repository
print("\n3. Clone repository:")
result = client.step(GitAction(action_type="clone_repo", repo_name="OpenEnv"))
print(f" Success: {result.observation.success}")
print(f" Message: {result.observation.message}")
print(f" Output: {result.observation.output}")

# Execute git commands
print("\n4. Execute git commands:")

git_commands = [
"status",
"log --oneline -5",
"branch -a",
]

for cmd in git_commands:
result = client.step(
GitAction(action_type="execute_git_command", command=cmd, working_dir="OpenEnv")
)
print(f"\n git {cmd}:")
print(f" Success: {result.observation.success}")
if result.observation.output:
# Show first few lines
lines = result.observation.output.strip().split("\n")[:5]
for line in lines:
print(f" {line}")
if len(result.observation.output.strip().split("\n")) > 5:
print(" ...")

# Check final state
print("\n5. Check final state:")
state = client.state()
print(f" episode_id: {state.episode_id}")
print(f" step_count: {state.step_count}")
print(f" gitea_ready: {state.gitea_ready}")

print("\n" + "-" * 60)
print("\n✓ All operations successful!")
print()

print("Cleaning up...")
client.close()
print("✓ Container stopped and removed")
print()

print("=" * 60)
print("Test completed successfully!")
print("=" * 60)

return True

except Exception as e:
print(f"\n❌ Test failed: {e}")
import traceback

traceback.print_exc()
return False


if __name__ == "__main__":
success = main()
exit(0 if success else 1)
83 changes: 83 additions & 0 deletions scripts/setup_shared_gitea.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/bash
# Setup script for shared Gitea instance
# This script starts Gitea, waits for it to be ready, and creates the admin user
# Requires: .env file with GITEA_USERNAME and GITEA_PASSWORD

set -e

# Load credentials from .env file
if [ -f .env ]; then
export $(cat .env | grep -E '^(GITEA_USERNAME|GITEA_PASSWORD)=' | xargs)
else
echo "❌ Error: .env file not found"
echo " Please copy .env.example to .env and configure credentials"
exit 1
fi

echo "====================================="
echo "Setting up shared Gitea instance"
echo "====================================="
echo

# Start Gitea with docker-compose
echo "1. Starting Gitea container..."
docker-compose -f src/envs/git_env/docker-compose.gitea.yml up -d

# Wait for Gitea to be healthy
echo "2. Waiting for Gitea to be ready..."
timeout=60
elapsed=0
while [ $elapsed -lt $timeout ]; do
if docker exec openenv-gitea curl -sf http://localhost:3000/ > /dev/null 2>&1; then
echo " ✓ Gitea is ready!"
break
fi
echo " Waiting... (${elapsed}s/${timeout}s)"
sleep 2
elapsed=$((elapsed + 2))
done

if [ $elapsed -ge $timeout ]; then
echo " ✗ Timeout waiting for Gitea"
exit 1
fi

# Initialize Gitea (POST to root URL)
echo "3. Initializing Gitea configuration..."
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better to model this as an MCP tool for this env eventually? RFC 004 touches upon this point. WDYT?
cc: @Darktex

docker exec openenv-gitea curl -s -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "db_type=sqlite3" \
-d "db_path=%2Fdata%2Fgitea%2Fgitea.db" \
-d "app_name=Gitea" \
-d "repo_root_path=%2Fdata%2Fgit%2Frepositories" \
-d "run_user=git" \
-d "domain=gitea" \
-d "http_port=3000" \
-d "app_url=http%3A%2F%2Fgitea%3A3000%2F" \
-d "log_root_path=%2Fdata%2Fgitea%2Flog" \
-d "offline_mode=on" \
http://localhost:3000/ > /dev/null || echo " (Config may already exist)"

# Create admin user
echo "4. Creating admin user ($GITEA_USERNAME)..."
docker exec openenv-gitea su git -c \
"gitea admin user create --username $GITEA_USERNAME --password $GITEA_PASSWORD --email ${GITEA_USERNAME}@local.env --admin" \
2>&1 | grep -q "already exists" && echo " ✓ User already exists" || echo " ✓ User created"

echo
echo "====================================="
echo "✓ Gitea setup complete!"
echo "====================================="
echo
echo "Gitea is now available at:"
echo " - Web UI: http://localhost:3000"
echo " - From containers: http://gitea:3000"
echo
echo "Admin credentials are configured from .env file"
echo
echo "To stop Gitea:"
echo " docker-compose -f src/envs/git_env/docker-compose.gitea.yml down"
echo
echo "To remove all data:"
echo " docker-compose -f src/envs/git_env/docker-compose.gitea.yml down -v"
echo
13 changes: 11 additions & 2 deletions src/core/http_env_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def from_docker_image(
cls: Type[EnvClientT],
image: str,
provider: Optional["ContainerProvider"] = None,
**kwargs: Any,
) -> EnvClientT:
"""
Create an environment client by spinning up a Docker container locally.
Expand All @@ -61,6 +62,8 @@ def from_docker_image(
Args:
image: Docker image name to run (e.g., "echo-env:latest")
provider: Container provider to use (defaults to LocalDockerProvider)
**kwargs: Additional arguments to pass to provider.start_container()
(e.g., env_vars, port)

Returns:
An instance of the client class connected to the running container
Expand All @@ -72,6 +75,12 @@ def from_docker_image(
>>> # Create environment from image
>>> env = CodingEnv.from_docker_image("coding-env:latest")
>>>
>>> # Create environment with custom env vars
>>> env = CodingEnv.from_docker_image(
... "coding-env:latest",
... env_vars={"MY_VAR": "value"}
... )
>>>
>>> # Use the environment
>>> result = env.reset()
>>> print(result.observation)
Expand All @@ -87,8 +96,8 @@ def from_docker_image(
if provider is None:
provider = LocalDockerProvider()

# 1. Start container
base_url = provider.start_container(image)
# 1. Start container with optional kwargs (e.g., env_vars, port)
base_url = provider.start_container(image, **kwargs)

# 2. Wait for server to be ready
provider.wait_for_ready(base_url)
Expand Down
7 changes: 6 additions & 1 deletion src/core/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

"""Core tools for code execution and other utilities."""

from .git_server_client import GitServerClient, RepoInfo
from .local_python_executor import PyExecutor

__all__ = ["PyExecutor"]
__all__ = [
"PyExecutor",
"GitServerClient",
"RepoInfo",
]
Loading