functions/public/Compare-GooSemVer.ps1
function Compare-GooSemVer { <# .SYNOPSIS Compare two SemVer strings. .DESCRIPTION Compare two SemVer strings and returns <, > or ==. Tests if the given Versions respect the Semantic Versioning guidelines, and throws an error if not. This Cmdlet accepts values from the pipeline. .PARAMETER DifferenceVersion Specifies the Versions that are compared to the reference Version. .PARAMETER ReferenceVersion Specifies a Version used as a reference for comparison. .EXAMPLE --- Example 1 Error case --- PS C:\> Compare-GooSemVer -DifferenceVersion 'a.b.c.' -ReferenceVersion '1.2.3' The value a.b.c. is not following the SemVer guidelines. .EXAMPLE --- Example 2 Valid comparisons --- PS C:\> Compare-GooSemVer -DifferenceVersion '1.2.3' -ReferenceVersion '1.2.3' == PS C:\> Compare-GooSemVer -DifferenceVersion '1.2.4' -ReferenceVersion '1.2.3' > PS C:\> @('1.0.0', '1.1.1-alpha', '2.0.0', '1.2.2', '1.2.3-alpha') | Compare-GooSemVer -ReferenceVersion '1.2.3' < < > < < .INPUTS System.String System.String .OUTPUTS System.String .NOTES For more information about Semantic Versioning 2.0.0, see this: https://semver.org/ The precedence is implemented by following this: https://semver.org/#spec-item-11 If it is hard to remember which Version is which, either Difference or Reference, you can look on the Compare-Object docs: https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/compare-object?view=powershell-7.1. Naming inspiration was drawn from there. The ReferenceVersion can accept only one value, where DifferenceVersion can take values from the pipeline. `@($a, $b, $c) | Compare-GooSemVer d` can be read as comparing a, b and c to d. d is the reference. #> [CmdletBinding()] [OutputType([string])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] [string] $DifferenceVersion, [Parameter(Mandatory = $true, Position = 1)] [string] $ReferenceVersion ) begin { if (-not ($ReferenceVersion -match $Script:GooSemVer.Rex)) { throw $Script:GooSemVer.InvalidVersionFormatMessage -f $ReferenceVersion } $referenceMatches = $Matches } process { if (-not ($DifferenceVersion -match $Script:GooSemVer.Rex)) { throw $Script:GooSemVer.InvalidVersionFormatMessage -f $DifferenceVersion } $differenceMatches = $Matches # https://semver.org/#spec-item-11 #2 foreach ($_ in @('major', 'minor', 'patch')) { if ($differenceMatches[$_] -lt $referenceMatches[$_]) { return '<' } if ($differenceMatches[$_] -gt $referenceMatches[$_]) { return '>' } } # https://semver.org/#spec-item-11 #3 if ($differenceMatches.ContainsKey('prerelease') -and -not ($referenceMatches.ContainsKey('prerelease'))) { return '<' } if (-not ($differenceMatches.ContainsKey('prerelease')) -and $referenceMatches.ContainsKey('prerelease')) { return '>' } # https://semver.org/#spec-item-11 #4 if ($differenceMatches.ContainsKey('prerelease') -and $referenceMatches.ContainsKey('prerelease')) { $differencePrerelease = $differenceMatches['prerelease'].Split('.') $referencePrerelease = $referenceMatches['prerelease'].Split('.') $commonLength = ($differencePrerelease.Count, $referencePrerelease.Count | Measure-Object -Minimum).Minimum for ($i = 0; $i -lt $commonLength; $i++) { # https://semver.org/#spec-item-11 #4.3 if (($differencePrerelease[$i] | isNumericIdentifier) -and -not ($referencePrerelease[$i] | isNumericIdentifier)) { return '<' } if (-not ($differencePrerelease[$i] | isNumericIdentifier) -and ($referencePrerelease[$i] | isNumericIdentifier)) { return '>' } # https://semver.org/#spec-item-11 #4.2 if ($differencePrerelease[$i] -lt $referencePrerelease[$i]) { return '<' } if ($differencePrerelease[$i] -gt $referencePrerelease[$i]) { return '>' } } # https://semver.org/#spec-item-11 #4.4 if ($differencePrerelease.Count -lt $referencePrerelease.Count) { return '<' } if ($differencePrerelease.Count -gt $referencePrerelease.Count) { return '>' } } return '=='; } } |