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
17 changes: 17 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ This means CLI flags override config file values, which override environment var
| -------- | -------- | ------------------------------- |
| `CONFIG` | No | Path to YAML configuration file |

!!! note "BALATROLLM_CONFIG Environment Variable"

The configuration file path can also be specified via the `BALATROLLM_CONFIG` environment variable:

```bash
export BALATROLLM_CONFIG="config/example.yaml"
balatrollm --model openai/gpt-5
```

This is the **only** `BALATROLLM_*` environment variable that does not have a corresponding CLI flag — the user can simply provide the config file path as a positional argument instead.

**Precedence:** If both the `CONFIG` argument and `BALATROLLM_CONFIG` are provided, the CLI argument takes precedence and `BALATROLLM_CONFIG` is ignored.

## Options

| CLI Flag | Environment Variable | Default | Description |
Expand Down Expand Up @@ -59,6 +72,10 @@ balatrollm --model openai/gpt-5
# Run with configuration file
balatrollm config/example.yaml

# Run with configuration file via environment variable
export BALATROLLM_CONFIG="config/example.yaml"
balatrollm --model openai/gpt-5

# Run with config file and override specific options
balatrollm config/example.yaml --model openai/gpt-5 --seed BBBBBBB
```
Expand Down
19 changes: 18 additions & 1 deletion src/balatrollm/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@

import argparse
import asyncio
import os
import sys
from pathlib import Path

from .config import Config, Task

# Environment variable for config file path (special: no corresponding CLI flag)
BALATROLLM_CONFIG_ENV = "BALATROLLM_CONFIG"


def create_parser() -> argparse.ArgumentParser:
"""Create the argument parser."""
Expand Down Expand Up @@ -83,14 +87,27 @@ async def execute(config: Config, tasks: list[Task]) -> int:
views_server.stop()


def _resolve_config_path(args_config: Path | None) -> Path | None:
"""Resolve config path: CLI arg takes precedence over BALATROLLM_CONFIG env var."""
if args_config is not None:
return args_config
env_config = os.environ.get(BALATROLLM_CONFIG_ENV)
if env_config:
return Path(env_config)
return None


def main() -> None:
"""Main entry point for balatrollm command."""
parser = create_parser()
args = parser.parse_args()

# Resolve config path: CLI arg > BALATROLLM_CONFIG env var
config_path = _resolve_config_path(args.config)

# Build config with precedence: env < yaml < args
try:
config = Config.load(yaml_path=args.config, args=args)
config = Config.load(yaml_path=config_path, args=args)
config.validate()
except (FileNotFoundError, ValueError) as e:
parser.error(str(e))
Expand Down
52 changes: 52 additions & 0 deletions tests/unit/test_cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Unit tests for the cli module."""

from pathlib import Path

import pytest

from balatrollm.cli import BALATROLLM_CONFIG_ENV, _resolve_config_path

# ============================================================================
# Test _resolve_config_path
# ============================================================================


class TestResolveConfigPath:
"""Tests for _resolve_config_path helper function."""

def test_cli_arg_returns_arg(self) -> None:
"""CLI argument should be returned when provided."""
cli_path = Path("/some/config.yaml")
result = _resolve_config_path(cli_path)
assert result == cli_path

def test_env_var_used_when_no_cli_arg(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""BALATROLLM_CONFIG env var should be used when CLI arg is None."""
monkeypatch.setenv(BALATROLLM_CONFIG_ENV, "/env/config.yaml")
result = _resolve_config_path(None)
assert result == Path("/env/config.yaml")

def test_cli_arg_takes_precedence_over_env(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""CLI argument should take precedence over BALATROLLM_CONFIG env var."""
monkeypatch.setenv(BALATROLLM_CONFIG_ENV, "/env/config.yaml")
cli_path = Path("/cli/config.yaml")
result = _resolve_config_path(cli_path)
assert result == cli_path

def test_none_when_no_cli_arg_and_no_env(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
"""None should be returned when neither CLI arg nor env var is set."""
monkeypatch.delenv(BALATROLLM_CONFIG_ENV, raising=False)
result = _resolve_config_path(None)
assert result is None

def test_empty_env_var_returns_none(self, monkeypatch: pytest.MonkeyPatch) -> None:
"""Empty BALATROLLM_CONFIG env var should be treated as unset."""
monkeypatch.setenv(BALATROLLM_CONFIG_ENV, "")
result = _resolve_config_path(None)
assert result is None
Loading