SemVer.psm1
if (-not ([System.Management.Automation.PSTypeName]'VersionComponent').Type) { Add-Type -TypeDefinition @" using System; using System.Text; public enum VersionComponent { Major = 0, Minor = 1, Patch = 2, Build = 3, Suffix = 4, SuffixBranch = 45, SuffixBuild = 50, SuffixRevision = 60 } public class SemVer { public int? GetComponent(VersionComponent c) { return VerNums.Length > (int)c ? VerNums[(int)c] : (int?)null; } public int? Major { get { return GetComponent(VersionComponent.Major); }} public int? Minor { get { return GetComponent(VersionComponent.Minor); }} public int? Patch { get { return GetComponent(VersionComponent.Patch); }} public string Version { get { return String.Join(".", VerNums); }} public string Suffix {get;set;} public string BranchName {get;set;} public int? BuildNum {get;set;} public string Revision {get;set;} public int[] VerNums {get;set;} public string RevSeparator {get;set;} public override string ToString() { var s = Version; if (!string.IsNullOrEmpty(Suffix)) { s += "-" + Suffix; } return s; } public string FormatSuffix(bool compatibilityMode = false) { StringBuilder sb = new StringBuilder(); if (!string.IsNullOrEmpty(BranchName)) { var b = BranchName; if (compatibilityMode) b = b.Replace("_","").Replace("+","").Replace(".","").Replace("/",""); // nuget does not tolerate these chars in suffix if (compatibilityMode) b = b.Substring(0, Math.Min(10,b.Length)); // max 20 chars for suffix: branch:10, build:3, rev:6, revseparator:1 sb.Append(b); } if (BuildNum != null) { if (sb.Length == 0) { sb.Append("build"); } if (!compatibilityMode) sb.Append("."); // nuget does not tolerate `.` in suffix if (sb.Length > 0 && Char.IsNumber(sb[sb.Length-1])) sb[sb.Length-1] = '-'; // separate branchname digits from build number sb.Append(BuildNum.Value.ToString("000")); } if (!string.IsNullOrEmpty(Revision)) { if (sb.Length > 0) { var revsep = RevSeparator; if (compatibilityMode) revsep = "-"; sb.Append(revsep); } var rev = Revision; if (compatibilityMode) rev = rev.Substring(0, Math.Min(6,rev.Length)); sb.Append(rev); } return sb.ToString(); } } "@ } $buildSuffixRegex = "(\.|build){0,1}(?<buildno>[0-9]{3})($|[-+][a-fA-F0-9]+$)" <# .SYNOPSIS Updates given component of a SemVer string .DESCRIPTION .PARAMETER ver SemVer string .PARAMETER component The version component you wish to update (Major, Minor, Patch) Also supports prerelease data with (SuffixBuild) and (SuffixRevision) .PARAMETER value Value for the specified version component. If value is not provided, the function will increment current number .PARAMETER compatibilityMode Use compatibility mode (useful for working with nuget packages) .EXAMPLE Update-Version "1.0.1" Patch 1.0.2 Increment Patch component of version 1.0.1 .NOTES .LINK #> function Update-Version { [CmdletBinding()] param( [Parameter(mandatory=$true)]$ver, [VersionComponent]$component = [VersionComponent]::Patch, $value = $null, [Alias("nuget")] [switch][bool] $compatibilityMode ) write-verbose "updating version $ver component $component to value $value" $semver = split-version $ver -compatibilityMode:$compatibilityMode $suffix = $semver.Suffix $lastNumIdx = $component if ($component -lt [VersionComponent]::Suffix) { while ($lastNumIdx -gt ($semver.vernums.length-1)) { write-verbose "$lastNumIdx > version length ($($semver.vernums.length-1)) => adding .0" $semver.vernums += @(0) } $lastNum = $semver.vernums[$lastNumIdx] if ($value -ne $null) { $lastNum = $value } else { $lastNum++ } $semver.vernums[$component] = $lastNum.ToString() #each lesser component should be set to 0 for($i = [int]$component + 1; $i -lt $semver.vernums.length; $i++) { $semver.vernums[$i] = 0 } if ($semver.BuildNum) { $ver2 = join-version $semver $ver2 = update-version $ver2 SuffixBuild -value 1 return $ver2 } } else { if ([string]::IsNullOrEmpty($suffix)) { $semver.buildnum = 0 } if ($component -eq [VersionComponent]::SuffixBuild) { if ($semver.buildnum -ne $null) { if ($value -ne $null) { $semver.buildnum = $value } else { $semver.buildnum++ } } else { $semver.buildnum = 1; #throw "suffix '$suffix' does not match $buildSuffixRegex pattern" } } if ($component -eq [VersionComponent]::SuffixRevision) { #write-verbose "setting suffix revision to '$value'" $revSeparator = "+" if ($compatibilityMode) { $revSeparator = "-" } $semver.RevSeparator = $revSeparator if (![string]::IsNullOrEmpty($semver.Revision)) { $oldrev = $semver.Revision } $semver.Revision = $value } if ($component -eq [VersionComponent]::SuffixBranch) { $semver.BranchName = $value } $sfx = $semver.FormatSuffix($compatibilityMode) if ($component -eq [VersionComponent]::Suffix) { $sfx = $value } write-verbose "updating suffix $($semver.suffix) => $sfx" $semver.suffix = $sfx } $ver2 = $semver.ToString() return $ver2 } function Split-Version($ver, [switch][bool] $compatibilityMode) { $null = $ver -match "(?<version>[0-9]+(\.[0-9]+)*)(-(?<suffix>.*)){0,1}" $version = $matches["version"] $suffix = $matches["suffix"] if ([string]::isnullorempty($suffix)) { $suffix = $null } $vernums = @($version.Split(@('.'))) $vernums = $vernums | % { [int]$_ } $buildnum = $null $buildnumidx = $null if ($suffix -match $buildSuffixRegex) { $m = [regex]::match($suffix, $buildSuffixRegex) $buildnumidx = $m.Index $buildnum = [int]$matches["buildno"] $suffixbuild = ".$($buildnum.ToString("000"))" } if ($suffix -match "^(?<branchname>[^.+]+)") { $branch = $matches["branchname"] if ($buildnumidx -ne $null -and $buildnumidx -gt 0) { $branch = $suffix.SubString(0,$buildnumidx) } elseif ($buildnumidx -eq 0) { $null = $suffix -match "^(?<branchname>[^.+\-0-9]+)" $branch = $matches["branchname"] } } $rev = $null $revSeparator = "+" if ($compatibilityMode) { $revSeparator = "-" } if ($suffix -match "\$revSeparator(?<rev>[a-fA-F0-9]+)$") { $rev = $Matches["rev"] } $r = new-object -type SemVer -property @{ Suffix = $suffix BuildNum = $buildnum Revision = $rev VerNums = $vernums RevSeparator = $revSeparator BranchName = $branch } return $r } function Join-Version([SemVer] $version) { return $version.ToString() } new-alias increment-version update-version |