Skip to content
Closed
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
56 changes: 35 additions & 21 deletions src/openenv/cli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"""

import sys
from collections.abc import Callable

import typer
from openenv.cli.commands import (
Expand All @@ -25,41 +26,54 @@
validate,
)

CommandCallback = Callable[..., object]
CommandSpec = tuple[str, str, CommandCallback]

# Create the main CLI app
app = typer.Typer(
name="openenv",
help="OpenEnv - An e2e framework for creating, deploying and using isolated execution environments for agentic RL training",
no_args_is_help=True,
)

# Register commands
app.command(name="init", help="Initialize a new OpenEnv environment")(init.init)
app.command(name="build", help="Build Docker images for OpenEnv environments")(
build.build
_COMMANDS_BEFORE_SKILLS: tuple[CommandSpec, ...] = (
("init", "Initialize a new OpenEnv environment", init.init),
("build", "Build Docker images for OpenEnv environments", build.build),
(
"validate",
"Validate environment structure and deployment readiness",
validate.validate,
),
(
"push",
"Push an OpenEnv environment to Hugging Face Spaces or custom registry",
push.push,
),
("serve", "Serve environments locally (TODO: Phase 4)", serve.serve),
("fork", "Fork (duplicate) a Hugging Face Space to your account", fork.fork),
)
app.command(
name="validate", help="Validate environment structure and deployment readiness"
)(validate.validate)
app.command(
name="push",
help="Push an OpenEnv environment to Hugging Face Spaces or custom registry",
)(push.push)
app.command(name="serve", help="Serve environments locally (TODO: Phase 4)")(
serve.serve
_COMMANDS_AFTER_SKILLS: tuple[CommandSpec, ...] = (
(
"collect",
"Collect rollouts from a deployed OpenEnv environment",
collect.collect,
),
)
app.command(
name="fork",
help="Fork (duplicate) a Hugging Face Space to your account",
)(fork.fork)


def _register_commands(commands: tuple[CommandSpec, ...]) -> None:
"""Register top-level Typer commands."""
for name, help_text, callback in commands:
app.command(name=name, help=help_text)(callback)


_register_commands(_COMMANDS_BEFORE_SKILLS)
app.add_typer(
skills.app,
name="skills",
help="Manage OpenEnv skills for AI assistants",
)
app.command(
name="collect",
help="Collect rollouts from a deployed OpenEnv environment",
)(collect.collect)
_register_commands(_COMMANDS_AFTER_SKILLS)


# Entry point for setuptools
Expand Down
19 changes: 18 additions & 1 deletion tests/test_cli/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,30 @@
from unittest.mock import patch

import pytest
from openenv.cli.__main__ import main
from openenv.cli.__main__ import app, main
from typer.testing import CliRunner


runner = CliRunner()


def test_cli_registers_expected_commands() -> None:
"""Test that the top-level CLI commands are registered."""
command_names = [command.name for command in app.registered_commands]
group_names = [group.name for group in app.registered_groups]

assert command_names == [
"init",
"build",
"validate",
"push",
"serve",
"fork",
"collect",
]
assert group_names == ["skills"]


def test_main_handles_keyboard_interrupt() -> None:
"""Test that main handles KeyboardInterrupt gracefully."""
with patch("openenv.cli.__main__.app") as mock_app:
Expand Down