Adding a New Strategy
Relevant source files
The following files were used as context for generating this wiki page:A Strategy in the IAC Recertification Engine defines the logic for grouping files that require recertification into one or more pull requests. The system is designed to be extensible, allowing developers to add new custom grouping strategies beyond the built-in options (per_file, per_pattern, per_committer, single_pr). This allows for flexible workflows, such as grouping files by team ownership, application, or any other custom business logic.
This document outlines the process for creating, integrating, and configuring a new PR Grouping Strategy. The core of this process involves implementing the Strategy interface and registering the new implementation within the strategy factory.
Core Concepts
Before creating a new strategy, it's important to understand the key interfaces and data structures involved in the grouping process. The Strategy interface defines the contract, taking a list of files needing review and outputting a set of groups, where each group corresponds to a single pull request.
Sources: iac-recert-engine/internal/strategy/strategy.go, iac-recert-engine/internal/types/types.go
The Strategy Interface
Any new strategy must implement the Strategy interface. This interface contains a single method, Group, which is responsible for the core grouping logic.
// iac-recert-engine/internal/strategy/strategy.go:11-13
type Strategy interface {
Group(results []types.RecertCheckResult) ([]types.FileGroup, error)
}
Sources: iac-recert-engine/internal/strategy/strategy.go:11-13
The diagram below shows the Strategy interface and the existing implementations. A new strategy would be a new struct that implements this interface.
Input: RecertCheckResult
The Group method receives a slice of RecertCheckResult structs. Each struct contains detailed information about a single file that has been analyzed for recertification.
| Field | Type | Description |
|---|---|---|
File | FileInfo | Information about the file, including path, author, and modification date. |
PatternName | string | The name of the configuration pattern that matched this file. |
DaysSince | int | Number of days since the file was last modified. |
Threshold | int | The recertification period in days for this file's pattern. |
Priority | string | Calculated priority (Critical, High, Medium, Low). |
NeedsRecert | bool | True if DaysSince is greater than or equal to Threshold. |
NextDueDate | time.Time | The calculated date for the next recertification. |
Sources: iac-recert-engine/internal/types/types.go:12-20
Output: FileGroup
The Group method must return a slice of FileGroup structs. Each FileGroup represents a set of files that will be included in a single pull request.
| Field | Type | Description |
|---|---|---|
ID | string | A unique identifier for the group. |
Strategy | string | The name of the strategy that created this group. |
Files | []RecertCheckResult | The list of files included in this group. |
Assignees | []string | (Optional) Suggested assignees for this group. |
Reviewers | []string | (Optional) Suggested reviewers for this group. |
Sources: iac-recert-engine/internal/types/types.go:22-28
Implementation Steps
Adding a new strategy involves implementing the interface, registering it in the factory function, and updating the configuration validation.
1. Define Your Strategy Struct
First, create a new struct in internal/strategy/strategy.go that will hold any necessary state for your strategy, such as a logger.
// Example for a new strategy, e.g., 'PerOwnerStrategy'
type PerOwnerStrategy struct {
logger *zap.Logger
// Add any other fields your strategy needs
}
2. Implement the Group Method
Next, implement the Group method for your new struct. This method should iterate through the RecertCheckResult slice, filter out files that do not need recertification, and partition the remaining files into FileGroups based on your custom logic.
The following diagram illustrates the general flow within a Group method.
3. Register the Strategy in the Factory
The NewStrategy function in internal/strategy/strategy.go acts as a factory. To make your new strategy available to the engine, you must add a new case to the switch statement that corresponds to the name of your strategy.
// iac-recert-engine/internal/strategy/strategy.go:15-27
func NewStrategy(cfg config.PRStrategyConfig, logger *zap.Logger) (Strategy, error) {
switch cfg.Type {
case "per_file":
return &PerFileStrategy{logger: logger}, nil
case "per_pattern":
return &PerPatternStrategy{logger: logger}, nil
case "per_committer":
return &PerCommitterStrategy{logger: logger}, nil
case "single_pr":
return &SinglePRStrategy{logger: logger}, nil
// Add your new strategy here
case "per_owner": // Example
return &PerOwnerStrategy{logger: logger}, nil
case "plugin":
return nil, fmt.Errorf("plugin strategy not implemented yet")
default:
return nil, fmt.Errorf("unknown strategy type: %s", cfg.Type)
}
}
Sources: iac-recert-engine/internal/strategy/strategy.go:15-27
4. Update Configuration Options
To allow users to select your new strategy in the configuration file, you must add its name to the oneof validator tag in the PRStrategyConfig struct.
// iac-recert-engine/internal/config/types.go:50-54
type PRStrategyConfig struct {
Type string `yaml:"type" mapstructure:"type" validate:"required,oneof=per_file per_pattern per_committer single_pr plugin per_owner"` // Add 'per_owner' here
MaxFilesPerPR int `yaml:"max_files_per_pr" mapstructure:"max_files_per_pr"`
PluginName string `yaml:"plugin_name" mapstructure:"plugin_name"` // For plugin strategy
}
Sources: iac-recert-engine/internal/config/types.go:50-54
5. Configure the New Strategy
Once implemented and registered, the new strategy can be enabled in the config.yaml file by setting the pr_strategy.type.
# example.config.yaml
# ...
# PR Grouping Strategy
# Options: per_file, per_pattern, per_committer, single_pr, plugin, per_owner
pr_strategy:
type: "per_owner" # Use your new strategy's name
# max_files_per_pr: 50 # Optional limit
Sources: iac-recert-engine/example.config.yaml:61-64
Integration with the Engine
The main Engine is responsible for initializing and executing the chosen strategy. The following sequence diagram shows how a strategy is integrated into the main application workflow.
This diagram illustrates that the Engine creates the strategy instance via the factory during initialization and later calls the Group method during its Run execution.
Sources: iac-recert-engine/cmd/ice/run.go:29-32, iac-recert-engine/internal/engine/engine.go:46-49, 126-130
Testing Your Strategy
It is crucial to add unit tests for your new strategy in internal/strategy/strategy_test.go. The tests should:
- Create a set of mock
types.RecertCheckResultdata that covers various scenarios for your grouping logic. - Instantiate your new strategy.
- Call the
Groupmethod with the mock data. - Assert that the returned
[]types.FileGroupslice is structured as expected, with the correct number of groups and the correct files in each group.
Existing tests for PerFileStrategy, PerPatternStrategy, etc., serve as excellent examples.
Sources: iac-recert-engine/internal/strategy/strategy_test.go
Summary
The strategy pattern employed by the IAC Recertification Engine provides a powerful mechanism for customizing how pull requests are created. By following the steps outlined—implementing the Strategy interface, registering it in the NewStrategy factory, updating the configuration validation, and adding tests—developers can seamlessly extend the engine's functionality to meet specific project or organizational needs.