mirror of
https://github.com/BerriAI/litellm.git
synced 2026-11-09 08:18:12 +00:00
* test(e2e): share the CLI version rule, env lockdown and rate limiter in tests/e2e/cli_harness Moves the npm release rule (now taking the package name), the env allowlist plus fresh HOME, and the per-upstream rate limiter out of claude_code so the Codex suite can reuse them. Claude Code behavior is unchanged, its CI, cron and Dockerfile paths now call the shared resolver with @anthropic-ai/claude-code * test(e2e): freeze the packument map and note the rate limiter self-test * test(e2e): race real processes on one rate limiter bucket so a missing lock fails Eight spawned processes start together and drain a bucket that never refills. With the flock they get exactly its burst between them, without it every attempt is granted * test(e2e): keep the old Claude Code version resolver path for the Buildkite runner images The runner Dockerfiles still run tests/e2e/claude_code/pr_gate_version_resolver.py and silently install @latest when it is missing, which drops the three day review window. The old path now forwards to cli_harness/npm_version_resolver.py
49 lines
1.4 KiB
Python
49 lines
1.4 KiB
Python
"""What a coding-agent CLI under test may see of the host.
|
|
|
|
The CI job holds the proxy's upstream credentials (provider keys, AWS, Azure, the database URL, GitHub tokens) in its
|
|
environment, and the cron VM runs as a real user with a populated home. A compromised CLI release, or a model told to
|
|
read a file, must reach neither, so the CLI gets only the allowlisted runtime variables plus what its suite passes in,
|
|
and a fresh empty HOME that is deleted when it exits.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import tempfile
|
|
from collections.abc import Generator, Mapping
|
|
from contextlib import contextmanager
|
|
from pathlib import Path
|
|
from types import MappingProxyType
|
|
from typing import Final
|
|
|
|
CLI_ENV_ALLOWLIST: Final = (
|
|
"PATH",
|
|
"USER",
|
|
"LOGNAME",
|
|
"SHELL",
|
|
"TERM",
|
|
"TMPDIR",
|
|
"LANG",
|
|
"LC_ALL",
|
|
"LC_CTYPE",
|
|
"NODE_PATH",
|
|
"NVM_DIR",
|
|
"NVM_BIN",
|
|
)
|
|
|
|
|
|
@contextmanager
|
|
def isolated_home(prefix: str) -> Generator[Path]:
|
|
home: Final = Path(tempfile.mkdtemp(prefix=prefix))
|
|
try:
|
|
yield home
|
|
finally:
|
|
shutil.rmtree(home, ignore_errors=True)
|
|
|
|
|
|
def locked_down_env(
|
|
*, home: Path, overrides: Mapping[str, str], source: Mapping[str, str] = os.environ
|
|
) -> Mapping[str, str]:
|
|
allowed: Final = {name: source[name] for name in CLI_ENV_ALLOWLIST if name in source}
|
|
return MappingProxyType({**allowed, "HOME": str(home), **overrides})
|