Noveris.CITools.psm1
<#
#> #Requires -Modules @{"ModuleName"="Noveris.Logger";"RequiredVersion"="0.6.1"} #Requires -Modules @{"ModuleName"="Noveris.Version";"RequiredVersion"="0.5.2"} #Requires -Modules @{"ModuleName"="Noveris.GitHubApi";"RequiredVersion"="0.1.2"} ######## # Global settings $InformationPreference = "Continue" $ErrorActionPreference = "Stop" Set-StrictMode -Version 2 ######## # Script variables $script:BuildDirectories = New-Object 'System.Collections.Generic.HashSet[string]' <# #> Function Use-EnvVar { [CmdletBinding()] param( [Parameter(mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(mandatory=$false)] [AllowEmptyString()] [AllowNull()] [string]$Default, [Parameter(mandatory=$false)] [ValidateNotNull()] [ScriptBlock]$Check ) process { $val = $Default if (Test-Path "Env:\$Name") { $val = (Get-Item "Env:\$Name").Value } elseif ($PSBoundParameters.keys -notcontains "Default") { Write-Error "Missing environment variable ($Name) and no default specified" } if ($PSBoundParameters.keys -contains "Check") { $ret = $val | ForEach-Object -Process $Check if (!$ret) { Write-Error "Source string (${Name}) failed validation" return } } $val } } <# #> Function Assert-SuccessExitCode { [CmdletBinding()] param( [Parameter(mandatory=$true)] [ValidateNotNull()] [int]$ExitCode, [Parameter(mandatory=$false)] [ValidateNotNull()] [int[]]$ValidCodes = @(0) ) process { if ($ValidCodes -notcontains $ExitCode) { Write-Error "Invalid exit code: ${ExitCode}" } } } <# #> Function Invoke-CIProfile { [CmdletBinding()] param( [Parameter(mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter(mandatory=$true)] [ValidateNotNull()] [HashTable]$Steps ) process { $scripts = New-Object 'System.Collections.Generic.LinkedList[PSCustomObject]' $components = New-Object 'System.Collections.Stack' $processedNames = New-Object 'System.Collections.Generic.HashSet[string]' $components.Push($Name) while ($components.Count -gt 0) { $content = $components.Pop() # Make sure we don't have empty content if ($null -eq $content) { Write-Error "Found null entry in list" continue } switch ($content.GetType().FullName) { "System.String" { $name = [string]$content if ($Steps.Keys -notcontains $name) { Write-Error "Missing step ($name) in definition" } # Check if we've processed this name before if ($processedNames.Contains($name)) { Write-Verbose ("Dependency ({0}) has already been processed" -f $name) continue } $processedNames.Add($name) | Out-Null Write-Verbose "Validating step $name" $step = [HashTable]($Steps[$name]) # Add post scripts if ($step.Keys -contains "PostScript") { $components.Push([PSCustomObject]@{ Name = "postscript_$name" Script = [ScriptBlock]($step["PostScript"]) }) } # Add names to stack (in reverse) if ($step.Keys -contains "Dependencies") { $list = ([string[]]($step["Dependencies"])) | ForEach-Object { $_ } [Array]::Reverse($list) $list | ForEach-Object { $components.Push($_) } } # Add pre scripts if ($step.Keys -contains "PreScript") { $components.Push([PSCustomObject]@{ Name = "prescript_$name" Script = [ScriptBlock]($step["PreScript"]) }) } break } "System.Management.Automation.PSCustomObject" { $scripts.Add($content) break } default { Write-Error "Unknown type found in list" break } } } $executions = New-Object 'System.Collections.Generic.HashSet[string]' foreach ($script in $scripts) { # Check if we've already run this script if ($executions.Contains($script.Name)) { Write-Verbose ("Script ({0}) already run" -f $script.Name) continue } Write-Information ("******** Processing " + $script.Name) try { & $script.Script *>&1 | Format-RecordAsString -DisplaySummary | Out-String -Stream Write-Information ("******** Finished " + $script.Name) Write-Information "" # Add the script to prevent further executions $executions.Add($script.Name) | Out-Null } catch { $ex = $_ # Display as information - Some systems don't show the exception properly Write-Information "Invoke-CIProfile failed with exception" Write-Information "Exception Information: $ex" Write-Information ("Exception is null?: " + ($null -eq $ex).ToString()) Write-Information "Exception Members:" $ex | Get-Member Write-Information "Exception Properties: " $ex | Format-List -property * # rethrow exception throw $ex } } } } <# #> Function Format-TemplateFile { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Template, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$Target, [Parameter(Mandatory=$true)] [Hashtable]$Content, [Parameter(Mandatory=$false)] [switch]$Stream = $false ) process { $dirPath = ([System.IO.Path]::GetDirectoryName($Target)) if (![string]::IsNullOrEmpty($dirPath)) { New-Item -ItemType Directory -Path $dirPath -EA Ignore } if (!$Stream) { $data = Get-Content $Template -Encoding UTF8 | Format-TemplateString -Content $Content $data | Out-File -Encoding UTF8 $Target } else { Get-Content $Template -Encoding UTF8 | Format-TemplateString -Content $Content | Out-File -Encoding UTF8 $Target } } } <# #> Function Format-TemplateString { [OutputType('System.String')] [CmdletBinding()] param( [Parameter(Mandatory=$true,ValueFromPipeline)] [AllowEmptyString()] [string]$TemplateString, [Parameter(Mandatory=$true)] [Hashtable]$Content ) process { $working = $TemplateString $Content.Keys | ForEach-Object { $working = $working.Replace($_, $Content[$_]) } $working } } <# #> Function Use-BuildDirectories { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding()] param( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [AllowEmptyCollection()] [string[]]$Directories ) process { $Directories | ForEach-Object { Use-BuildDirectory $_ } } } <# #> Function Use-BuildDirectory { [CmdletBinding()] param( [Parameter(mandatory=$true,ValueFromPipeline)] [ValidateNotNullOrEmpty()] [string]$Path ) process { New-Item -ItemType Directory $Path -EA Ignore | Out-Null Write-Information "Using build directory: ${Path}" if (!(Test-Path $Path -PathType Container)) { Write-Error "Target does not exist or is not a directory" } try { Get-Item $Path -Force | Out-Null } catch { Write-Error $_ } $script:BuildDirectories.Add($Path) | Out-Null } } <# #> Function Clear-BuildDirectory { [CmdletBinding()] param( [Parameter(mandatory=$true,ValueFromPipeline)] [ValidateNotNullOrEmpty()] [string]$Path ) process { Use-BuildDirectory -Path $Path | Out-Null Write-Information "Clearing directory: ${Path}" Get-ChildItem -Path $Path | ForEach-Object { Remove-Item -Path $_.FullName -Recurse -Force } } } <# #> Function Clear-BuildDirectories { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding()] param( ) process { Write-Information "Clearing build directories" Get-BuildDirectories Get-BuildDirectories | ForEach-Object { Clear-BuildDirectory $_ } } } <# #> Function Get-BuildDirectories { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] [CmdletBinding()] param( ) process { $script:BuildDirectories | ForEach-Object { $_ } } } <# #> Function Invoke-Native { [CmdletBinding()] param( [Parameter(Mandatory=$true)] [ValidateNotNull()] [ScriptBlock]$Script, [Parameter(Mandatory=$false)] [ValidateNotNull()] [int[]]$ValidExitCodes = @(0), [Parameter(Mandatory=$false)] [switch]$IgnoreExitCode = $false, [Parameter(Mandatory=$false)] [switch]$RedirectStderr = $false ) process { $LASTEXITCODE = 0 if ($RedirectStderr) { & $Script 2>&1 | Out-String -Stream } else { & $Script | Out-String -Stream } $exitCode = $LASTEXITCODE if (!$IgnoreExitCode -and $ValidExitCodes -notcontains $exitCode) { Write-Error "Invalid exit code returned: $exitCode" } } } |