"""This module defines the configuration model for the pipeline."""
from enum import StrEnum
from typing import Self
from pydantic import BaseModel, Field, model_validator
[docs]
class ModeEnum(StrEnum):
"""Mode of execution for the pipeline.
- 'tests': Run tests only.
- 'benchmarks': Run benchmarks only.
"""
TESTS = "tests"
BENCHMARKS = "benchmarks"
[docs]
class GranularityEnum(StrEnum):
"""Granularity of execution for the pipeline.
- 'commits': Run tests for each commit.
- 'branches': Run tests for each branch.
- 'tags': Run tests for each tag.
"""
COMMITS = "commits"
BRANCHES = "branches"
TAGS = "tags"
[docs]
class RepositoryDefinition(BaseModel):
"""Definition of the repository to be tested."""
url: str = Field(
...,
description="URL of the repository to be tested.",
examples=["https://github.com/signalfx/signalfx-java"],
)
branch: str | None = Field(None, description="Branch to be used for testing.", examples=["main"])
clone_options: list[str] | None = Field(
default=None,
description="Additional options for cloning the repository.",
examples=[["--depth", "1"]],
)
[docs]
class ExecutionPlanDefinition(BaseModel):
"""Execution plan for the pipeline."""
mode: ModeEnum = Field(
default=ModeEnum.TESTS,
description="Execution mode: either 'tests' or 'benchmarks'.",
examples=["tests"],
)
compile_commands: list[str] | None = Field(
default=None,
description="List of compile commands required when using benchmarks mode.",
examples=[["make all"]],
)
granularity: GranularityEnum = Field(
default=GranularityEnum.COMMITS,
description="Granularity at which to execute tests (commits, branches, or tags).",
examples=["commits"],
)
pre_command: str | None = Field(
default=None,
description="Command to execute before running tests.",
examples=["mvn clean test-compile"],
)
pre_command_condition_files: set[str] = Field(
default_factory=set,
description="Set of file paths that trigger the pre_command if modified.",
examples=[{"pom.xml", "build.gradle"}],
)
test_command: str = Field(..., description="Command to execute tests.", examples=["mvn surefire:test"])
test_command_path: str = Field(default="", description="Path where the test command should be executed.", examples=["."])
ignore_failures: bool = Field(
default=True,
description="Flag indicating whether to ignore failures.",
examples=[False],
)
post_command: str | None = Field(
default=None,
description="Command to execute after tests are run.",
examples=["echo 'Tests completed'"],
)
num_commits: int | None = Field(default=None, description="Number of commits to test.", examples=[15])
num_runs: int = Field(default=1, description="Number of times each test should be run.", examples=[30])
num_repeats: int = Field(default=1, description="Number of repetitions for each test execution.", examples=[1])
batch_size: int = Field(default=100, description="Number of tasks to execute in one batch.", examples=[100])
randomize_tasks: bool = Field(
default=False,
description="Flag indicating whether the test tasks should be executed in random order.",
examples=[True],
)
oldest_commit: str | None = Field(
default=None,
description="The hash of the oldest commit to consider.",
examples=["db1ba94c8a8aae10b021ffd8532bd9d8fc42ae10"],
)
newest_commit: str | None = Field(
default=None,
description="The hash of the newest commit to consider.",
examples=["f47ac10b-58cc-4372-a567-0e02b2c3d479"],
)
execute_common_tests: bool = Field(
default=False,
description="Flag indicating whether to execute common tests in addition to specific ones.",
examples=[False],
)
[docs]
@model_validator(mode="after")
def check_compile_commands_for_benchmarks(self: Self) -> Self:
"""Ensure that compile_commands is provided when benchmarks mode is selected.
Returns:
Self: The instance of the class.
Raises:
ValueError: If compile_commands is not provided in benchmarks mode.
"""
if self.mode == ModeEnum.BENCHMARKS and not self.compile_commands:
raise ValueError("compile_commands must be provided for 'benchmarks' mode.") # noqa: TRY003
return self
[docs]
class LimitsDefinition(BaseModel):
"""Limits for the pipeline execution."""
temperature_safe_limit: int = Field(
...,
description="The maximum safe operating temperature (in milli-degrees).",
examples=[65000],
)
[docs]
class RegressionDetectionDefinition(BaseModel):
"""Configuration for regression detection context in the pipeline.
This model specifies the minimum number of commits to include before and/or after a candidate commit.
"""
min_commits_before: int = Field(
default=1,
description="Minimum number of commits to include before a candidate commit as baseline.",
examples=[1],
)
min_commits_after: int = Field(
default=0,
description="Minimum number of commits to include after a candidate commit for confirmation.",
examples=[0],
)
[docs]
class PipelineConfig(BaseModel):
"""Configuration model for the entire pipeline."""
config_version: str = Field(default="1.0.0", description="Version of the configuration schema.", examples=["1.0.0"])
repo: RepositoryDefinition = Field(..., description="Repository configuration details.")
execution_plan: ExecutionPlanDefinition = Field(..., description="Execution plan for running tests or benchmarks.")
limits: LimitsDefinition = Field(
...,
description="Limits for pipeline execution.",
examples=[{"temperature_safe_limit": 65000, "energy_regression_percent": 20}],
)
tracked_file_extensions: set[str] = Field(
...,
description="Set of file extensions to track for changes.",
examples=[{"java", "xml", "properties", "yaml", "yml"}],
)
ignored_directories: set[str] = Field(
default_factory=set,
description="Set of directories to ignore during commit filtering.",
examples=[{"target", "build", "out"}],
)
cpu_thermal_file: str = Field(
...,
description="Path to the CPU thermal file for monitoring temperature.",
examples=["/sys/class/hwmon/hwmon5/temp1_input"],
)
setup_commands: list[str] | None = Field(
default=None,
description="List of shell commands to setup the environment.",
examples=[["export JAVA_HOME=/usr/lib/jvm/java-8-openjdk", "export PATH=$JAVA_HOME/bin:$PATH"]],
)
regression_detection: RegressionDetectionDefinition = Field(
default_factory=RegressionDetectionDefinition,
description="Configuration for regression detection context commits.",
)
timeout: int = Field(
default=120,
description="Timeout for executions.",
examples=[120],
)