Files
cleveragents-core/noxfile.py
T

326 lines
9.5 KiB
Python

import sys
import nox
# Global configuration
DEFAULT_PYTHON = "3.13"
SUPPORTED_PYTHONS = ["3.13"]
nox.options.reuse_existing_virtualenvs = True
nox.options.error_on_external_run = True
# =============================================================================
# QUICK DEVELOPMENT SESSIONS (Fast - Run daily)
# =============================================================================
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def lint(session):
"""Check code formatting and linting."""
session.install("ruff")
session.run("ruff", "check", "src/", "scripts/", "examples/")
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def format(session):
"""Format code with ruff."""
session.install("ruff")
session.run("ruff", "format", ".")
# session.run("ruff", "check", "--fix", ".")
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def typecheck(session):
"""Check types with pyright."""
session.install("pyright")
session.install("-e", ".")
session.run("pyright", stderr=sys.stdout)
@nox.session(python=SUPPORTED_PYTHONS, reuse_venv=True, venv_backend="uv")
def unit_tests(session):
"""Run BDD tests with Behave."""
session.install("-e", ".[tests]")
session.install("behave")
# Run Behave BDD tests (excluding discovery tests)
python_executable = session.bin + "/python"
# Check if a specific feature file is passed
args = []
if session.posargs and session.posargs[0].endswith(".feature"):
# If a specific feature file is passed, run only that file
args = [
python_executable,
"-m",
"behave",
"-q",
"--tags=not @discovery",
*session.posargs,
]
else:
# Default: run all features with posargs appended
args = [
python_executable,
"-m",
"behave",
"-q",
"--tags=not @discovery",
"features/",
*session.posargs,
]
session.run(*args)
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def docs(session):
"""Build documentation with MkDocs."""
session.install("-e", ".[docs]")
session.run("mkdocs", "build")
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def serve_docs(session):
"""Serve docs locally for development."""
session.install("-e", ".[docs]")
session.run("mkdocs", "serve", "--dev-addr", "0.0.0.0:8000")
@nox.session(python=SUPPORTED_PYTHONS, reuse_venv=True, venv_backend="uv")
def build(session):
"""Build the wheel distribution."""
session.install("build")
session.run("python", "-m", "build", "--wheel")
@nox.session(python=SUPPORTED_PYTHONS, reuse_venv=True, venv_backend="uv")
def integration_tests(session):
"""Run Robot Framework integration tests (excluding discovery tests)."""
session.install("-e", ".[tests]")
# If posargs provided, run only those specific files/suites
if session.posargs:
session.run(
"robot",
"--outputdir",
"build/reports/robot",
"--loglevel",
"INFO",
"--report",
"report.html",
"--log",
"log.html",
"--xunit",
"xunit.xml",
"--exclude",
"slow",
"--exclude",
"discovery", # Exclude discovery tests
"--exitonfailure", # Exit immediately on test failure
*session.posargs,
external=False, # Ensure exit code is checked
success_codes=[0], # Only code 0 is success
)
else:
# Default: run all tests in robot/ directory
session.run(
"robot",
"--outputdir",
"build/reports/robot",
"--loglevel",
"INFO",
"--report",
"report.html",
"--log",
"log.html",
"--xunit",
"xunit.xml",
"--exclude",
"slow",
"--exclude",
"discovery", # Exclude discovery tests
# "--exitonfailure", # Exit immediately on test failure
"robot/",
external=False, # Ensure exit code is checked
success_codes=[0], # Only code 0 is success
)
@nox.session(python=SUPPORTED_PYTHONS, reuse_venv=True, venv_backend="uv")
def slow_integration_tests(session):
"""Run Robot Framework integration tests."""
session.install("-e", ".[tests]")
session.run(
"robot",
"--outputdir",
"build/reports/robot",
"--loglevel",
"INFO",
"--report",
"report.html",
"--log",
"log.html",
"--xunit",
"xunit.xml",
"robot/",
*session.posargs,
)
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def coverage_report(session):
"""Generate coverage report from Behave tests."""
session.install("-e", ".[tests]")
# Clean up any existing coverage data
session.run("coverage", "erase")
# Run Behave tests with coverage
session.run(
"coverage",
"run",
"--source=src",
"--omit=src/cleveragents/discovery/*",
"-m",
"behave",
"features/",
"-q",
"--tags=not @discovery",
)
# Combine parallel coverage data (required when parallel=true in config)
try:
session.run("coverage", "combine")
except Exception:
# No parallel data to combine
pass
# Generate and display coverage reports
session.run("coverage", "report", "--show-missing", "--fail-under=85")
session.run("coverage", "html")
session.run("coverage", "xml")
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def discovery_tests(session):
"""Run Phase 0 discovery tests (tagged with @discovery)."""
session.install("-e", ".[tests]")
session.install("behave")
python_executable = session.bin + "/python"
# Run only tests tagged with @discovery
session.run(
python_executable,
"-m",
"behave",
"-q",
"--tags=@discovery",
"features/",
*session.posargs,
)
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def discovery_coverage(session):
"""Generate coverage report for discovery tests."""
session.install("-e", ".[tests]")
# Clean up any existing coverage data
session.run("coverage", "erase")
# Run only discovery tests with coverage
session.run(
"coverage",
"run",
"--source=src/cleveragents/discovery",
"-m",
"behave",
"features/",
"-q",
"--tags=@discovery",
)
# Combine parallel coverage data (required when parallel=true in config)
try:
session.run("coverage", "combine")
except Exception:
# No parallel data to combine
pass
# Generate and display coverage reports
session.run("coverage", "report", "--show-missing")
session.run("coverage", "html")
session.run("coverage", "xml")
@nox.session(python=SUPPORTED_PYTHONS, reuse_venv=True, venv_backend="uv")
def discovery_integration(session):
"""Run Robot Framework integration tests tagged with 'discovery'."""
session.install("-e", ".[tests]")
# If posargs provided, run only those specific files/suites with discovery tag
if session.posargs:
session.run(
"robot",
"--outputdir",
"build/reports/robot_discovery",
"--loglevel",
"INFO",
"--report",
"discovery_report.html",
"--log",
"discovery_log.html",
"--xunit",
"discovery_xunit.xml",
"--include",
"discovery", # Include only discovery tests
"--exitonfailure", # Exit immediately on test failure
*session.posargs,
external=False, # Ensure exit code is checked
success_codes=[0], # Only code 0 is success
)
else:
# Default: run all discovery tests in robot/ directory
session.run(
"robot",
"--outputdir",
"build/reports/robot_discovery",
"--loglevel",
"INFO",
"--report",
"discovery_report.html",
"--log",
"discovery_log.html",
"--xunit",
"discovery_xunit.xml",
"--include",
"discovery", # Include only discovery tests
"robot/",
external=False, # Ensure exit code is checked
success_codes=[0], # Only code 0 is success
)
@nox.session(python=DEFAULT_PYTHON, reuse_venv=True, venv_backend="uv")
def benchmark(session):
"""Run Airspeed Velocity benchmarks and publish results."""
session.install("-e", ".[tests]")
config_path = "asv.conf.json"
session.run("asv", "run", "--show-stderr", "--verbose", f"--config={config_path}")
session.run("asv", "publish", f"--config={config_path}")
# Sessions to run by default when running `nox` without arguments
nox.options.sessions = [
"lint", # ~5-10 seconds
"format", # ~5-10 seconds (auto-fixes issues)
"typecheck", # ~10-20 seconds
"unit_tests", # ~30-60 seconds (without ML deps)
"integration_tests",
"docs", # ~10-30 seconds
"build", # ~5-10 seconds
"benchmark", # ASV benchmarks for performance tracking
"coverage_report", # ~30-60 seconds (without ML deps)
]