function Format-ParseChocoPackages { <# .SYNOPSIS Returns a list of package objects parsed from choco-style "name|version" strings. .DESCRIPTION Takes a list of package names and optional versions and returns array of PSObjects with properties containing the Name and Version (if entered) parsed. .PARAMETER Text Can be either an array of package names with versions to parse or a NewLine-delimited string of package names and versions. .PARAMETER Delimiter Delimiter to use when splitting the package name from the version in the $Text parameter. Defaults to "|" .PARAMETER Validate Switch to use SemVer regular expression validation on the Version. Also performs duplicate check on 'PackageName but different Version' passed into -Text parameter. .Example Format-ParseChocoPackages -Text @("alkami.api.afx|1.7.1-pre100","alkami.api.afx|1.7.1-pre100") -Validate Format-ParseChocoPackages -Text alkami.api.afx|1.7.1 -Validate Format-ParseChocoPackages -Text alkami.api.afx Format-ParseChocoPackages -Text chocolatey|v1.7.1.0 #> [CmdletBinding()] [OutputType([System.Object])] [OutputType([System.Collections.ArrayList])] Param( [Parameter(Mandatory = $true)] [AllowNull()] [object]$Text, [Parameter(Mandatory = $false)] [string]$Delimiter = "|", [Parameter(Mandatory = $false)] [switch]$Validate ) if (!$Text) { $packages = @() return $packages } $logLead = (Get-LogLeadName) # Detect if it's a single string passed in and split it into a string-array per line. if(!($Text -is [array])) { $Text = $Text.Split([Environment]::NewLine) } # Detect any exact duplicate lines and remove them. # If this is coming from Classify-Packages, we've already trimmed this. But if it isn't, we still want this unique to work as expected. if($Validate){ $Text = $Text.Trim() | Sort-Object | Get-Unique } Write-Verbose "$logLead : Parsing $($Text.Count) choco packages..." $chocoPackages = [System.Collections.ArrayList]::new() $packageMap = @{}; $Text | ForEach-Object { if(!([string]::IsNullOrEmpty($_))) { Write-Verbose "$logLead : Parsing $_" $option = [System.StringSplitOptions]::RemoveEmptyEntries $splitItem = $_.Trim().Split($Delimiter, $option) Write-Verbose "$logLead : Package Name parsed to [$($splitItem[0])]" Write-Verbose "$logLead : Package Version parsed to [$($splitItem[1])]" if($Validate -and $splitItem.Count -gt 2) { Write-Error "Package [$($_.Trim())] passed in is not in the correct SemVer3 format. Is there more than one package on a line?" } # Make sure that the version isn't malformed. Should be in the proper Semver format. Regular Epxression taken from https://semver.org if($Validate -and $null -ne $splitItem[1] -and $splitItem[1] -cnotmatch "^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$") { Write-Error "Version [$($splitItem[1])] of module [$($splitItem[0])] passed in is not in the correct SemVer3 format." } $properties = @{ Name = $splitItem[0]; Version = $splitItem[1]; Feed = $null; Tags = $null; IsService = $null; StartMode = $null; IsValid = $false;} $pkg = New-Object -TypeName PSObject -Prop $properties # Test if the package object is already in the array. Return error if same package name but different versions are in the input array. if($Validate) { $lowerName = $pkg.Name.ToLower() if($packageMap.ContainsKey($lowerName)) { if($packageMap[$lowerName] -ne $pkg.Version) { Write-Error "Package [$($pkg.Name)] is in the list more than once with differing versions. Please review your package parameters and include only the correct version" Write-Host "##teamcity[buildProblem description='Multiple versions of $($pkg.Name) are in the install list' identity='$($pkg.Name)']" } } $packageMap[$lowerName] = $pkg.Version } $chocoPackages.Add($pkg) | Out-Null } } Write-Verbose "$logLead : Successfully parsed $($chocoPackages.Count) packages." return $chocoPackages }