lib/ValidationRules.ps1
|
############################################################################# # ValidationRules.ps1 - Rule engine and helper functions ############################################################################# . "$PSScriptRoot/StateModel.ps1" class ValidationRule { [string]$Name # Unique identifier (e.g., "patch_release_required") [string]$Description # Human-readable description [int]$Priority # Lower values run first [string]$Category # Grouping category (ref_type, version_tracking, releases, etc.) [scriptblock]$Condition # Returns items to validate [scriptblock]$Check # Returns $true when the item is already valid [scriptblock]$CreateIssue # Creates a ValidationIssue when the item is invalid } function Get-ValidationRule { param( [string]$RulesPath = (Join-Path -Path $PSScriptRoot -ChildPath "rules") ) if (-not (Test-Path $RulesPath)) { return @() } $rules = @() $ruleFiles = Get-ChildItem -Path $RulesPath -Recurse -Filter "*.ps1" | Where-Object { $_.Name -notmatch '\.Tests\.ps1$' } foreach ($file in $ruleFiles) { $loaded = . $file.FullName if ($loaded -is [ValidationRule]) { $rules += $loaded } } return $rules | Sort-Object -Property Priority, Name } function Invoke-ValidationRule { param( [Parameter(Mandatory)] [RepositoryState]$State, [hashtable]$Config = @{}, [ValidationRule[]]$Rules ) $pathToUse = if ($Rules) { $null } else { Join-Path -Path $PSScriptRoot -ChildPath "rules" } $rulesToRun = if ($Rules) { $Rules } else { Get-ValidationRule -RulesPath $pathToUse } if (-not $rulesToRun -or $rulesToRun.Count -eq 0) { return @() } $addedIssues = @() foreach ($rule in ($rulesToRun | Sort-Object -Property Priority, Name)) { if (-not ($rule -is [ValidationRule])) { continue } if (-not $rule.Condition) { throw "Rule '$($rule.Name)' is missing a Condition scriptblock." } if (-not $rule.Check) { throw "Rule '$($rule.Name)' is missing a Check scriptblock." } if (-not $rule.CreateIssue) { throw "Rule '$($rule.Name)' is missing a CreateIssue scriptblock." } $items = & $rule.Condition $State $Config if ($null -eq $items) { continue } foreach ($item in @($items)) { $isValid = & $rule.Check $item $State $Config if (-not $isValid) { $issue = & $rule.CreateIssue $item $State $Config if ($issue -is [ValidationIssue]) { $State.AddIssue($issue) $addedIssues += $issue } } } } return $addedIssues } # Helper function to check if a version is a prerelease by looking up its Release function Test-IsPrerelease { param( [Parameter(Mandatory)][RepositoryState]$State, [Parameter(Mandatory)][VersionRef]$VersionRef ) # Look up the release for this version $release = $State.Releases | Where-Object { $_.TagName -eq $VersionRef.Version } | Select-Object -First 1 # If no release exists, it's not a prerelease if (-not $release) { return $false } return $release.IsPrerelease } function Get-HighestPatchForMajor { param( [Parameter(Mandatory)][RepositoryState]$State, [Parameter(Mandatory)][int]$Major, [bool]$ExcludePrereleases = $false ) $patches = ($State.Tags + $State.Branches) | Where-Object { $_.IsPatch -and -not $_.IsIgnored -and $_.Major -eq $Major -and (-not $ExcludePrereleases -or -not (Test-IsPrerelease -State $State -VersionRef $_)) } return $patches | Sort-Object -Property Major, Minor, Patch -Descending | Select-Object -First 1 } function Get-HighestPatchForMinor { param( [Parameter(Mandatory)][RepositoryState]$State, [Parameter(Mandatory)][int]$Major, [Parameter(Mandatory)][int]$Minor, [bool]$ExcludePrereleases = $false ) $patches = ($State.Tags + $State.Branches) | Where-Object { $_.IsPatch -and -not $_.IsIgnored -and $_.Major -eq $Major -and $_.Minor -eq $Minor -and (-not $ExcludePrereleases -or -not (Test-IsPrerelease -State $State -VersionRef $_)) } return $patches | Sort-Object -Property Major, Minor, Patch -Descending | Select-Object -First 1 } function Get-HighestMinorForMajor { param( [Parameter(Mandatory)][RepositoryState]$State, [Parameter(Mandatory)][int]$Major, [bool]$ExcludePrereleases = $false ) $candidates = ($State.Tags + $State.Branches) | Where-Object { -not $_.IsIgnored -and $_.Major -eq $Major -and (-not $ExcludePrereleases -or -not (Test-IsPrerelease -State $State -VersionRef $_)) } if (-not $candidates) { return $null } $patchCandidates = $candidates | Where-Object { $_.IsPatch } if ($patchCandidates) { return $patchCandidates | Sort-Object -Property Minor, Patch -Descending | Select-Object -First 1 } $minorCandidates = $candidates | Where-Object { $_.IsMinor } return $minorCandidates | Sort-Object -Property Minor -Descending | Select-Object -First 1 } function Get-GlobalHighestPatch { param( [Parameter(Mandatory)][RepositoryState]$State, [bool]$ExcludePrereleases = $false ) $patches = ($State.Tags + $State.Branches) | Where-Object { $_.IsPatch -and -not $_.IsIgnored -and (-not $ExcludePrereleases -or -not (Test-IsPrerelease -State $State -VersionRef $_)) } return $patches | Sort-Object -Property Major, Minor, Patch -Descending | Select-Object -First 1 } |