Something went wrong. Try again.
The agent that grows with you
Something went wrong. Try again.
4.8 kB · 104 lines
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105#!/usr/bin/env bash# Canonical test runner for hermes-agent. Run this instead of calling# `pytest` directly to guarantee your local run matches CI behavior.## What this script enforces:# * -n 4 xdist workers (CI has 4 cores; -n auto diverges locally)# * TZ=UTC, LANG=C.UTF-8, PYTHONHASHSEED=0 (deterministic)# * Credential env vars blanked (conftest.py also does this, but this# is belt-and-suspenders for anyone running `pytest` outside of# our conftest path — e.g. calling pytest on a single file)# * Proper venv activation## Usage:# scripts/run_tests.sh # full suite# scripts/run_tests.sh tests/agent/ # one directory# scripts/run_tests.sh tests/agent/test_foo.py::TestClass::test_method# scripts/run_tests.sh --tb=long -v # pass-through pytest args
set -euo pipefail
# ── Locate repo root ────────────────────────────────────────────────────────# Works whether this is the main checkout or a worktree.SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
# ── Activate venv ───────────────────────────────────────────────────────────# Prefer a .venv in the current tree, fall back to the main checkout's venv# (useful for worktrees where we don't always duplicate the venv).VENV=""for candidate in "$REPO_ROOT/.venv" "$REPO_ROOT/venv" "$HOME/.hermes/hermes-agent/venv"; do if [ -f "$candidate/bin/activate" ]; then VENV="$candidate" break fidone
if [ -z "$VENV" ]; then echo "error: no virtualenv found in $REPO_ROOT/.venv or $REPO_ROOT/venv" >&2 exit 1fi
PYTHON="$VENV/bin/python"
# ── Ensure pytest-split is installed (required for shard-equivalent runs) ──if ! "$PYTHON" -c "import pytest_split" 2>/dev/null; then echo "→ installing pytest-split into $VENV" "$PYTHON" -m pip install --quiet "pytest-split>=0.9,<1"fi
# ── Hermetic environment ────────────────────────────────────────────────────# Mirror what CI does in .github/workflows/tests.yml + what conftest.py does.# Unset every credential-shaped var currently in the environment.while IFS='=' read -r name _; do case "$name" in *_API_KEY|*_TOKEN|*_SECRET|*_PASSWORD|*_CREDENTIALS|*_ACCESS_KEY| \ *_SECRET_ACCESS_KEY|*_PRIVATE_KEY|*_OAUTH_TOKEN|*_WEBHOOK_SECRET| \ *_ENCRYPT_KEY|*_APP_SECRET|*_CLIENT_SECRET|*_CORP_SECRET|*_AES_KEY| \ AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|AWS_SESSION_TOKEN|FAL_KEY| \ GH_TOKEN|GITHUB_TOKEN) unset "$name" ;; esacdone < <(env)
# Unset HERMES_* behavioral vars too.unset HERMES_YOLO_MODE HERMES_INTERACTIVE HERMES_QUIET HERMES_TOOL_PROGRESS \ HERMES_TOOL_PROGRESS_MODE HERMES_MAX_ITERATIONS HERMES_SESSION_PLATFORM \ HERMES_SESSION_CHAT_ID HERMES_SESSION_CHAT_NAME HERMES_SESSION_THREAD_ID \ HERMES_SESSION_SOURCE HERMES_SESSION_KEY HERMES_GATEWAY_SESSION \ HERMES_PLATFORM HERMES_INFERENCE_PROVIDER HERMES_MANAGED HERMES_DEV \ HERMES_CONTAINER HERMES_EPHEMERAL_SYSTEM_PROMPT HERMES_TIMEZONE \ HERMES_REDACT_SECRETS HERMES_BACKGROUND_NOTIFICATIONS HERMES_EXEC_ASK \ HERMES_HOME_MODE 2>/dev/null || true
# Pin deterministic runtime.export TZ=UTCexport LANG=C.UTF-8export LC_ALL=C.UTF-8export PYTHONHASHSEED=0
# ── Worker count ────────────────────────────────────────────────────────────# CI uses `-n auto` on ubuntu-latest which gives 4 workers. A 20-core# workstation with `-n auto` gets 20 workers and exposes test-ordering# flakes that CI will never see. Pin to 4 so local matches CI.WORKERS="${HERMES_TEST_WORKERS:-4}"
# ── Run pytest ──────────────────────────────────────────────────────────────cd "$REPO_ROOT"
# If the first argument starts with `-` treat all args as pytest flags;# otherwise treat them as test paths.ARGS=("$@")
echo "▶ running pytest with $WORKERS workers, hermetic env, in $REPO_ROOT"echo " (TZ=UTC LANG=C.UTF-8 PYTHONHASHSEED=0; all credential env vars unset)"
# -o "addopts=" clears pyproject.toml's `-n auto` so our -n wins.exec "$PYTHON" -m pytest \ -o "addopts=" \ -n "$WORKERS" \ --ignore=tests/integration \ --ignore=tests/e2e \ -m "not integration" \ "${ARGS[@]}"