Viscalyx.Common.psm1
#Region '.\prefix.ps1' -1 $script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules/DscResource.Common' Import-Module -Name $script:dscResourceCommonModulePath $script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' #EndRegion '.\prefix.ps1' 5 #Region '.\Public\ConvertTo-AnsiSequence.ps1' -1 <# .SYNOPSIS Converts a string value to an ANSI escape sequence. .DESCRIPTION The ConvertTo-AnsiSequence command converts a string value to an ANSI escape sequence. It is used to format text with ANSI escape codes for color and formatting in console output. .PARAMETER Value The string value to be converted to an ANSI escape sequence. .INPUTS System.String .OUTPUTS System.String .EXAMPLE ConvertTo-AnsiSequence -Value "31" This example converts the string value "31" to its ANSI escape sequence. .NOTES This function supports ANSI escape codes for color and formatting. It checks if the input string matches the pattern of an ANSI escape sequence and converts it accordingly. If the input string does not match the pattern, it is returned as is. #> function ConvertTo-AnsiSequence { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true, Position = 0)] [AllowEmptyString()] [AllowNull()] [System.String] $Value ) if ($Value) { if ($Value -match "^(?:`e)?\[?([0-9;]+)m?$") { $Value = "`e[" + $Matches[1] + 'm' } } return $Value } #EndRegion '.\Public\ConvertTo-AnsiSequence.ps1' 53 #Region '.\Public\ConvertTo-DifferenceString.ps1' -1 <# .SYNOPSIS Converts two strings into a difference string, highlighting the differences between them. .DESCRIPTION The ConvertTo-DifferenceString command takes two strings, a reference string and a difference string, and converts them into a difference string that highlights the differences between the two strings. The function compares the byte values of each character in the strings and outputs the differences in a formatted manner. It supports customizing the indicators, labels, colors, and encoding type. .PARAMETER ReferenceString Specifies the reference string to compare against. .PARAMETER DifferenceString Specifies the difference string to compare. .PARAMETER EqualIndicator Specifies the indicator to use for equal bytes. Default is '=='. .PARAMETER NotEqualIndicator Specifies the indicator to use for not equal bytes. Default is '!='. .PARAMETER HighlightStart Specifies the ANSI escape sequence to start highlighting. Default is "[31m" (red color). .PARAMETER HighlightEnd Specifies the ANSI escape sequence to end highlighting. Default is "[0m" (reset color). .PARAMETER ReferenceLabel Specifies the label for the reference string. Default is 'Expected:'. .PARAMETER DifferenceLabel Specifies the label for the difference string. Default is 'But was:'. .PARAMETER NoColumnHeader Specifies whether to exclude the column header from the output. .PARAMETER NoLabels Specifies whether to exclude the labels from the output. .PARAMETER ReferenceLabelAnsi Specifies the ANSI escape sequence to apply to the reference label. .PARAMETER DifferenceLabelAnsi Specifies the ANSI escape sequence to apply to the difference label. .PARAMETER ColumnHeaderAnsi Specifies the ANSI escape sequence to apply to the column header. .PARAMETER ColumnHeaderResetAnsi Specifies the ANSI escape sequence to reset the column header. .PARAMETER EncodingType Specifies the encoding type to use for converting the strings to byte arrays. Default is 'UTF8'. .EXAMPLE PS> ConvertTo-DifferenceString -ReferenceString 'Hello' -DifferenceString 'Hallo' Expected: But was: ---------------------------------------------------------------- -------------------------------------------------------- Bytes Ascii Bytes Ascii ----- ----- ----- ----- 48 65 6C 6C 6F Hello == 48 61 6C 6C 6F Hallo Converts the reference string 'Hello' and the difference string 'Hallo' into a difference string, highlighting the differences. .INPUTS None. .OUTPUTS System.String. #> function ConvertTo-DifferenceString { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseBOMForUnicodeEncodedFile', '', Justification = 'This file is not intended to be saved as a Unicode-encoded file even though it has unicode characters, if that is a problem that can be re-evaluated.')] [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true)] [AllowEmptyString()] [System.String] $ReferenceString, [Parameter(Mandatory = $true)] [AllowEmptyString()] [System.String] $DifferenceString, [Parameter()] [ValidateLength(0, 2)] [System.String] $EqualIndicator = '==', [Parameter()] [ValidateLength(0, 2)] [System.String] $NotEqualIndicator = '!=', [Parameter()] [System.String] $HighlightStart = '[31m', # Default to red color [Parameter()] [System.String] $HighlightEnd = '[0m', # Default to reset color [Parameter()] [System.String] $ReferenceLabel = 'Expected:', [Parameter()] [System.String] $DifferenceLabel = 'But was:', [Parameter()] [System.Management.Automation.SwitchParameter] $NoColumnHeader, [Parameter()] [System.Management.Automation.SwitchParameter] $NoLabels, [Parameter()] [System.String] $ReferenceLabelAnsi = '', [Parameter()] [System.String] $DifferenceLabelAnsi = '', [Parameter()] [System.String] $ColumnHeaderAnsi = '', [Parameter()] [System.String] $ColumnHeaderResetAnsi = '', [Parameter()] [ValidateSet('ASCII', 'BigEndianUnicode', 'Default', 'Unicode', 'UTF32', 'UTF7', 'UTF8')] [System.String] $EncodingType = 'UTF8' ) $HighlightStart = ConvertTo-AnsiSequence -Value $HighlightStart $HighlightEnd = ConvertTo-AnsiSequence -Value $HighlightEnd $ReferenceLabelAnsi = ConvertTo-AnsiSequence -Value $ReferenceLabelAnsi $DifferenceLabelAnsi = ConvertTo-AnsiSequence -Value $DifferenceLabelAnsi $ColumnHeaderAnsi = ConvertTo-AnsiSequence -Value $ColumnHeaderAnsi $ColumnHeaderResetAnsi = ConvertTo-AnsiSequence -Value $ColumnHeaderResetAnsi # Handle empty string or single character indicator $NotEqualIndicator = $NotEqualIndicator.PadRight(2) $EqualIndicator = $EqualIndicator.PadRight(2) # Convert the strings to byte arrays using the specified encoding $referenceBytes = ([System.Text.Encoding]::$EncodingType).GetBytes($ReferenceString) $differenceBytes = ([System.Text.Encoding]::$EncodingType).GetBytes($DifferenceString) # Determine the maximum length of the two byte arrays $maxLength = [Math]::Max($referenceBytes.Length, $differenceBytes.Length) # Initialize arrays to hold hex values and characters $refHexArray = @() $refCharArray = @() $diffHexArray = @() $diffCharArray = @() # Escape $HighlightStart and $HighlightEnd for regex matching $escapedHighlightStart = [regex]::Escape($HighlightStart) $escapedHighlightEnd = [regex]::Escape($HighlightEnd) # Output the labels if NoLabels is not specified if (-not $NoLabels) { "$($ReferenceLabelAnsi)$($ReferenceLabel)$($HighlightEnd) $($DifferenceLabelAnsi)$($DifferenceLabel)$($HighlightEnd)" ('-' * 64) + (' ' * 8) + ('-' * 64) # Output a line of dashes under the labels } # Output the column header once with dashes underline if NoColumnHeader is not specified if (-not $NoColumnHeader) { "$($ColumnHeaderAnsi)Bytes Ascii Bytes Ascii$($ColumnHeaderResetAnsi)" "$($ColumnHeaderAnsi)----- ----- ----- -----$($ColumnHeaderResetAnsi)" } # Loop through each byte in the arrays up to the maximum length for ($i = 0; $i -lt $maxLength; $i++) { # Get the byte and character for the reference string if ($i -lt $referenceBytes.Length) { $refByte = $referenceBytes[$i] $refHex = '{0:X2}' -f $refByte $refChar = [char]$refByte } else { $refHex = ' ' $refChar = ' ' } # Get the byte and character for the difference string if ($i -lt $differenceBytes.Length) { $diffByte = $differenceBytes[$i] $diffHex = '{0:X2}' -f $diffByte $diffChar = [char]$diffByte } else { $diffHex = ' ' $diffChar = ' ' } # Highlight differences if ($refHex -ne $diffHex) { $refHex = "$($HighlightStart)$refHex$($HighlightEnd)" $refChar = "$($HighlightStart)$refChar$($HighlightEnd)" $diffHex = "$($HighlightStart)$diffHex$($HighlightEnd)" $diffChar = "$($HighlightStart)$diffChar$($HighlightEnd)" } # Replace control characters with their Unicode representations in the output $refChar = $refChar` -replace "`0", '␀' ` -replace "`a", '␇' ` -replace "`b", '␈' ` -replace "`t", '␉' ` -replace "`f", '␌' ` -replace "`r", '␍' ` -replace "`n", '␊' ` -replace "(?!$($escapedHighlightStart))(?!$($escapedHighlightEnd))`e", '␛' $diffChar = $diffChar ` -replace "`0", '␀' ` -replace "`a", '␇' ` -replace "`b", '␈' ` -replace "`t", '␉' ` -replace "`f", '␌' ` -replace "`r", '␍' ` -replace "`n", '␊' ` -replace "(?!$($escapedHighlightStart))(?!$($escapedHighlightEnd))`e", '␛' # Add to arrays $refHexArray += $refHex $refCharArray += $refChar $diffHexArray += $diffHex $diffCharArray += $diffChar # Output the results in groups of 16 if (($i + 1) % 16 -eq 0 -or $i -eq $maxLength - 1) { # Pad arrays to ensure they have 16 elements while ($refHexArray.Count -lt 16) { $refHexArray += ' ' } while ($refCharArray.Count -lt 16) { $refCharArray += ' ' } while ($diffHexArray.Count -lt 16) { $diffHexArray += ' ' } while ($diffCharArray.Count -lt 16) { $diffCharArray += ' ' } $refHexLine = ($refHexArray -join ' ') $refCharLine = ($refCharArray -join '') $diffHexLine = ($diffHexArray -join ' ') $diffCharLine = ($diffCharArray -join '') # Determine if the line was highlighted $indicator = if ($refHexLine -match $escapedHighlightStart -or $diffHexLine -match $escapedHighlightStart) { $NotEqualIndicator } else { $EqualIndicator } # Output the results in the specified format '{0} {1} {2} {3} {4}' -f $refHexLine, $refCharLine, $indicator, $diffHexLine, $diffCharLine # Clear arrays for the next group of 16 $refHexArray = @() $refCharArray = @() $diffHexArray = @() $diffCharArray = @() } } } #EndRegion '.\Public\ConvertTo-DifferenceString.ps1' 307 #Region '.\Public\ConvertTo-RelativePath.ps1' -1 <# .SYNOPSIS Converts an absolute path to a relative path. .DESCRIPTION The ConvertTo-RelativePath command takes an absolute path and converts it to a relative path based on the current location. If the absolute path starts with the current location, the function removes the current location from the beginning of the path and inserts a '.' to indicate the relative path. .PARAMETER AbsolutePath Specifies the absolute path that needs to be converted to a relative path. .PARAMETER CurrentLocation Specifies the current location used as a reference for converting the absolute path to a relative path. If not specified, the function uses the current location obtained from Get-Location. .EXAMPLE ConvertTo-RelativePath -AbsolutePath '/source/Viscalyx.Common/source/Public/ConvertTo-RelativePath.ps1' -CurrentLocation "/source/Viscalyx.Common" Returns "./source/Public/ConvertTo-RelativePath.ps1", which is the relative path of the given absolute path based on the current location. .INPUTS [System.String] .OUTPUTS [System.String] #> function ConvertTo-RelativePath { [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] [System.String] $AbsolutePath, [Parameter(Position = 1)] [System.String] $CurrentLocation ) begin { if (-not $PSBoundParameters.ContainsKey('CurrentLocation')) { $CurrentLocation = (Get-Location).Path } } process { $relativePath = $AbsolutePath if ($relativePath.StartsWith($CurrentLocation)) { $relativePath = $relativePath.Substring($CurrentLocation.Length).Insert(0, '.') } return $relativePath } } #EndRegion '.\Public\ConvertTo-RelativePath.ps1' 66 #Region '.\Public\Get-ModuleVersion.ps1' -1 <# .SYNOPSIS Retrieves the version of a PowerShell module. .DESCRIPTION The Get-ModuleVersion command retrieves the version of a PowerShell module. It accepts a module name or a PSModuleInfo object as input and returns the module version as a string. .PARAMETER Module Specifies the module for which to retrieve the version. This can be either a module name or a PSModuleInfo object. .EXAMPLE Get-ModuleVersion -Module 'MyModule' Retrieves the version of the module named "MyModule". .EXAMPLE $moduleInfo = Get-Module -Name 'MyModule' Get-ModuleVersion -Module $moduleInfo Retrieves the version of the module specified by the PSModuleInfo object $moduleInfo. .INPUTS [System.Object] Accepts a module name or a PSModuleInfo object as input. .OUTPUTS [System.String] Returns the module version as a string. #> function Get-ModuleVersion { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position = 0)] [System.Object] $Module ) process { $moduleInfo = $null $moduleVersion = $null if ($Module -is [System.String]) { $moduleInfo = Get-Module -Name $Module -ErrorAction 'Stop' if (-not $moduleInfo) { Write-Error -Message "Cannot find the module '$Module'. Make sure it is loaded into the session." } } elseif ($Module -is [System.Management.Automation.PSModuleInfo]) { $moduleInfo = $Module } else { Write-Error -Message "Invalid parameter type. The parameter 'Module' must be either a string or a PSModuleInfo object." } if ($moduleInfo) { $moduleVersion = $moduleInfo.Version.ToString() $previewReleaseTag = $moduleInfo.PrivateData.PSData.Prerelease if ($previewReleaseTag) { $moduleVersion += '-{0}' -f $previewReleaseTag } } return $moduleVersion } } #EndRegion '.\Public\Get-ModuleVersion.ps1' 83 #Region '.\Public\Get-NumericalSequence.ps1' -1 <# .SYNOPSIS Retrieves numerical sequences from a given set of numbers. .DESCRIPTION The Get-NumericalSequence command retrieves numerical sequences from a given set of numbers. It identifies consecutive numbers and groups them into ranges. .PARAMETER Number Specifies the number to be processed. This parameter is mandatory and can be provided via the pipeline. .OUTPUTS System.Object[] An array of PSCustomObject objects representing the numerical sequences. Each object contains the Start and End properties, indicating the start and end numbers of a sequence. .EXAMPLE Get-NumericalSequence -Number 1, 2, 3, 5, 6, 7, 10 Returns: Start End ----- --- 1 3 5 7 10 #> function Get-NumericalSequence { [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [System.Int32] $Number ) begin { $ranges = @() $start = $null $end = $null } process { if ($null -eq $start) { $start = $Number $end = $Number } elseif ($Number -eq $end + 1) { $end = $Number } else { if ($start -eq $end) { $end = $null } $ranges += [PSCustomObject] @{ Start = $start End = $end } $start = $Number $end = $Number } } end { if ($null -ne $start) { if ($start -eq $end) { $end = $null } $ranges += [PSCustomObject] @{ Start = $start End = $end } } $ranges } } #EndRegion '.\Public\Get-NumericalSequence.ps1' 93 #Region '.\Public\Get-PSReadLineHistory.ps1' -1 <# .SYNOPSIS Retrieves the PSReadLine history content. .DESCRIPTION The Get-PSReadLineHistory function retrieves the content of the PSReadLine history file. By default, it returns the entire history content, but you can specify a pattern to filter the results. .PARAMETER Pattern Specifies a pattern to filter the history content. Only lines matching the pattern will be returned. .EXAMPLE Get-PSReadLineHistory Returns the entire content of the PSReadLine history file. .EXAMPLE Get-PSReadLineHistory -Pattern "git" Returns only the lines from the PSReadLine history file that contain the word "git". .INPUTS None .OUTPUTS System.String .NOTES This function requires the PSReadLine module to be installed. .LINK https://docs.microsoft.com/en-us/powershell/module/psreadline/ #> function Get-PSReadLineHistory { [CmdletBinding()] param ( [Parameter(Position = 0)] [System.String] $Pattern ) $historyPath = (Get-PSReadLineOption).HistorySavePath $historyContent = Get-Content -Path $historyPath if ($Pattern) { $historyContent = $historyContent | Select-Object -SkipLast 1 | Select-String -Pattern $Pattern -Raw } $historyContent } #EndRegion '.\Public\Get-PSReadLineHistory.ps1' 59 #Region '.\Public\Invoke-PesterJob.ps1' -1 <# .SYNOPSIS Runs Pester tests using a job-based approach. .DESCRIPTION The `Invoke-PesterJob` command runs Pester tests using a job-based approach. It allows you to specify various parameters such as the test path, root path, module name, output verbosity, code coverage path, and more. Its primary purpose is to run Pester tests in a separate job to avoid polluting the current session with PowerShell classes and project specific assemblies which can cause issues when building the project. It is helpful for projects based on the Sampler project template, but it can also be used for other projects. .PARAMETER Path Specifies one or more paths to the Pester test files. If not specified, the current location is used. This also has tab completion support. Just write part of the test script file name and press tab to get a list of available test files matching the input, or if only one file matches, it will be auto-completed. .PARAMETER RootPath Specifies the root path for the Pester tests. If not specified, the current location is used. .PARAMETER Tag Specifies the tags to filter the Pester tests. .PARAMETER ModuleName Specifies the name of the module to test. If not specified, it will be inferred based on the project type. .PARAMETER Output Specifies the output verbosity level. Valid values are 'Normal', 'Detailed', 'None', 'Diagnostic', and 'Minimal'. Default is 'Detailed'. .PARAMETER CodeCoveragePath Specifies the paths to one or more the code coverage files (script or module script files). If not provided the default path for code coverage is the content of the built module. This parameter also has tab completion support. Just write part of the script file name and press tab to get a list of available script files matching the input, or if only one file matches, it will be auto-completed. .PARAMETER SkipCodeCoverage Indicates whether to skip code coverage. .PARAMETER PassThru Indicates whether to pass the Pester result object through. .PARAMETER ShowError Indicates whether to display detailed error information. When using this to debug a test it is recommended to run as few tests as possible, or just the test having issues, to limit the amount of error information displayed. .PARAMETER SkipRun Indicates whether to skip running the tests, this just runs the discovery phase. This is useful when you want to see what tests would be run without actually running them. To actually make use of this, the PassThru parameter should also be specified. Suggest to also use the parameter SkipCodeCoverage. .PARAMETER BuildScriptPath Specifies the path to the build script. If not specified, it defaults to 'build.ps1' in the root path. This is used to ensure that the test environment is configured correctly, for example required modules are available in the session. It is also used to ensure to find the specific Pester module used by the project. .PARAMETER BuildScriptParameter Specifies a hashtable with the parameters to pass to the build script. Defaults to parameter 'Task' with a value of 'noop'. .EXAMPLE $invokePesterJobParameters = @{ Path = './tests/Unit/DSC_SqlAlias.Tests.ps1' CodeCoveragePath = './output/builtModule/SqlServerDsc/0.0.1/DSCResources/DSC_SqlAlias/DSC_SqlAlias.psm1' } Invoke-PesterJob @invokePesterJobParameters Runs the Pester test DSC_SqlAlias.Tests.ps1 located in the 'tests/Unit' folder. The code coverage is based on the code in the DSC_SqlAlias.psm1 file. .EXAMPLE $invokePesterJobParameters = @{ Path = './tests' RootPath = 'C:\Projects\MyModule' Tag = 'Unit' Output = 'Detailed' CodeCoveragePath = 'C:\Projects\MyModule\coverage' } Invoke-PesterJob @invokePesterJobParameters Runs Pester tests located in the 'tests' directory of the 'C:\Projects\MyModule' root path. Only tests with the 'Unit' tag will be executed. Detailed output will be displayed, and code coverage will be collected from the 'C:\Projects\MyModule\coverage' directory. .EXAMPLE $invokePesterJobParameters = @{ Path = './tests/Unit' SkipRun = $true SkipCodeCoverage = $true PassThru = $true } Invoke-PesterJob @invokePesterJobParameters Runs the discovery phase on all the Pester tests files located in the 'tests/Unit' folder and outputs the Pester result object. .NOTES This function requires the Pester module to be imported. If the module is not available, it will attempt to run the build script to ensure the required modules are available in the session. #> function Invoke-PesterJob { # cSpell: ignore Runspaces [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseUsingScopeModifierInNewRunspaces', '', Justification = 'This is a false positive. The script block is used in a job and does not use variables from the parent scope, they are passed in ArgumentList.')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('AvoidWriteErrorStop', '', Justification = 'If $PSCmdlet.ThrowTerminatingError were used, the error would not stop any command that would call Invoke-PesterJob.')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', '', Justification = 'Argument completers always need the same parameters even if they are not used in the argument completer script.')] [Diagnostics.CodeAnalysis.SuppressMessageAttribute('DscResource.AnalyzerRules\Measure-Hashtable', '', Justification = 'The hashtable must be format as is to work when documentation is being generated by PlatyPS.')] [Alias('ipj')] [CmdletBinding()] param ( [Parameter(Position = 0)] [ArgumentCompleter( { <# This scriptblock is used to provide tab completion for the Path parameter. The scriptblock could be a command, but then it would need to be a public command. Also, if anything goes wrong in the completer scriptblock, it will just fail silently and not provide any completion results. #> param ( [Parameter()] $CommandName, [Parameter()] $ParameterName, [Parameter()] $WordToComplete, [Parameter()] $CommandAst, [Parameter()] $FakeBoundParameters ) # This parameter is from Invoke-PesterJob. if (-not $FakeBoundParameters.ContainsKey('RootPath')) { $RootPath = (Get-Location).Path } $testRoot = Join-Path -Path $RootPath -ChildPath 'tests/unit' $values = (Get-ChildItem -Path $testRoot -Recurse -Filter '*.tests.ps1' -File).FullName foreach ($val in $values) { if ($val -like "*$WordToComplete*") { New-Object -Type System.Management.Automation.CompletionResult -ArgumentList @( (ConvertTo-RelativePath -AbsolutePath $val -CurrentLocation $RootPath) # completionText (Split-Path -Path $val -Leaf) -replace '\.[Tt]ests.ps1' # listItemText 'ParameterValue' # resultType $val # toolTip ) } } })] [ValidateNotNullOrEmpty()] [System.String[]] $Path = (Get-Location).Path, [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $RootPath = (Get-Location).Path, [Parameter()] [ValidateNotNullOrEmpty()] [System.String[]] $Tag, [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $ModuleName, [Parameter()] [System.String] [ValidateSet('Normal', 'Detailed', 'None', 'Diagnostic', 'Minimal')] $Output, [Parameter(Position = 1)] [ArgumentCompleter( { <# This scriptblock is used to provide tab completion for the CodeCoveragePath parameter. The scriptblock could be a command, but then it would need to be a public command. Also, if anything goes wrong in the completer scriptblock, it will just fail silently and not provide any completion results. #> param ( [Parameter()] $CommandName, [Parameter()] $ParameterName, [Parameter()] $WordToComplete, [Parameter()] $CommandAst, [Parameter()] $FakeBoundParameters ) # This parameter is from Invoke-PesterJob. if (-not $FakeBoundParameters.ContainsKey('RootPath')) { $RootPath = (Get-Location).Path } # TODO: builtModule should be dynamic. $builtModuleCodePath = @( Join-Path -Path $RootPath -ChildPath 'output/builtModule' ) $paths = Get-ChildItem -Path $builtModuleCodePath -Recurse -Include @('*.psm1', '*.ps1') -File -ErrorAction 'SilentlyContinue' # Filter out the external Modules directory. $values = $paths.FullName -notmatch 'Modules' $leafRegex = [regex]::new('([^\\/]+)$') foreach ($val in $values) { $leaf = $leafRegex.Match($val).Groups[1].Value if ($leaf -like "*$WordToComplete*") { New-Object -Type System.Management.Automation.CompletionResult -ArgumentList @( (ConvertTo-RelativePath -AbsolutePath $val -CurrentLocation $RootPath) # completionText $leaf -replace '\.(ps1|psm1)' # listItemText 'ParameterValue' # resultType $val # toolTip ) } } })] [ValidateNotNullOrEmpty()] [System.String[]] $CodeCoveragePath, [Parameter()] [System.Management.Automation.SwitchParameter] $SkipCodeCoverage, [Parameter()] [System.Management.Automation.SwitchParameter] $PassThru, [Parameter()] [System.Management.Automation.SwitchParameter] $ShowError, [Parameter()] [System.Management.Automation.SwitchParameter] $SkipRun, [Parameter()] [ValidateScript({ if (-not (Test-Path $_ -PathType 'Leaf')) { throw "The file path '$_' does not exist or is a container." } $true })] [System.String] $BuildScriptPath, [Parameter()] [System.Collections.Hashtable] $BuildScriptParameter = @{ Task = 'noop' } ) if (-not $PSBoundParameters.ContainsKey('BuildScriptPath')) { $BuildScriptPath = Join-Path -Path $RootPath -ChildPath 'build.ps1' } $pesterModuleVersion = $null do { $triesCount = 0 try { $importedPesterModule = Import-Module -Name 'Pester' -MinimumVersion '4.10.1' -ErrorAction 'Stop' -PassThru $pesterModuleVersion = $importedPesterModule | Get-ModuleVersion <# Assuming that the project is a Sampler project if the Sampler module is available in the session. Also assuming that a Sampler build task has been run prior to running the command. #> $isSamplerProject = $null -ne (Get-Module -Name 'Sampler') } catch { $triesCount++ if ($triesCount -eq 1 -and (Test-Path -Path $BuildScriptPath)) { Write-Information -MessageData 'Could not import Pester. Running build script to make sure required modules is available in session. This can take a few seconds.' -InformationAction 'Continue' # Redirect all streams to $null, except the error stream (stream 2) & $BuildScriptPath @buildScriptParameter 2>&1 4>&1 5>&1 6>&1 > $null } else { Write-Error -ErrorRecord $_ -ErrorAction 'Stop' } } } until ($importedPesterModule) Write-Information -MessageData ('Using imported Pester v{0}.' -f $pesterModuleVersion) -InformationAction 'Continue' if (-not $PSBoundParameters.ContainsKey('ModuleName')) { if ($isSamplerProject) { $ModuleName = Get-SamplerProjectName -BuildRoot $RootPath } else { $ModuleName = (Get-Item -Path $RootPath).BaseName } } $testResultsPath = Join-Path -Path $RootPath -ChildPath 'output/testResults' if (-not $PSBoundParameters.ContainsKey('CodeCoveragePath')) { # TODO: Should be possible to use default coverage paths for a module that is not based on Sampler. if ($isSamplerProject) { $BuiltModuleBase = Get-SamplerBuiltModuleBase -OutputDirectory "$RootPath/output" -BuiltModuleSubdirectory 'builtModule' -ModuleName $ModuleName # TODO: This does not take into account any .ps1 files in the module. # TODO: This does not take into account any other .psm1 files in the module, e.g. MOF-based DSC resources. $CodeCoveragePath = '{0}/*/{1}.psm1' -f $BuiltModuleBase, $ModuleName } } if ($importedPesterModule.Version.Major -eq 4) { $pesterConfig = @{ Script = $Path } } else { $pesterConfig = New-PesterConfiguration -Hashtable @{ CodeCoverage = @{ Enabled = $true Path = $CodeCoveragePath OutputPath = (Join-Path -Path $testResultsPath -ChildPath 'PesterJob_coverage.xml') UseBreakpoints = $false } Run = @{ Path = $Path } } } if ($PSBoundParameters.ContainsKey('Output')) { if ($importedPesterModule.Version.Major -eq 4) { $pesterConfig.Show = $Output } else { $pesterConfig.Output.Verbosity = $Output } } else { if ($importedPesterModule.Version.Major -eq 4) { $pesterConfig.Show = 'All' } else { $pesterConfig.Output.Verbosity = 'Detailed' } } # Turn off code coverage if the user has specified that they don't want it if ($SkipCodeCoverage.IsPresent) { # Pester v4: By not passing code paths the code coverage is disabled. # Pester v5: By setting the Enabled property to false the code coverage is disabled. if ($importedPesterModule.Version.Major -ge 5) { $pesterConfig.CodeCoverage.Enabled = $false } } else { # Pester 4: By passing code paths the code coverage is enabled. if ($importedPesterModule.Version.Major -eq 4) { $pesterConfig.CodeCoverage = $CodeCoveragePath } } if ($PassThru.IsPresent) { if ($importedPesterModule.Version.Major -eq 4) { $pesterConfig.PassThru = $true } else { $pesterConfig.Run.PassThru = $true } } if ($SkipRun.IsPresent) { # This is only supported in Pester v5 or higher. if ($importedPesterModule.Version.Major -ge 5) { $pesterConfig.Run.SkipRun = $true } } if ($PSBoundParameters.ContainsKey('Tag')) { if ($importedPesterModule.Version.Major -eq 4) { $pesterConfig.Tag = $Tag } else { $pesterConfig.Filter.Tag = $Tag } } Start-Job -ScriptBlock { [CmdletBinding()] param ( [Parameter(Mandatory = $true, Position = 0)] [System.Object] $PesterConfiguration, [Parameter(Mandatory = $true, Position = 1)] [System.Management.Automation.SwitchParameter] $ShowError, [Parameter(Mandatory = $true, Position = 2)] [System.Version] $PesterVersion, [Parameter(Mandatory = $true, Position = 3)] [System.String] $BuildScriptPath, [Parameter(Mandatory = $true, Position = 4)] [System.Collections.Hashtable] $BuildScriptParameter ) Write-Information -MessageData 'Running build task ''noop'' inside the job to setup the test pipeline.' -InformationAction 'Continue' & $BuildScriptPath @buildScriptParameter if ($ShowError.IsPresent) { $Error.Clear() $ErrorView = 'DetailedView' } if ($PesterVersion.Major -eq 4) { Invoke-Pester @PesterConfiguration } else { Invoke-Pester -Configuration $PesterConfiguration } if ($ShowError.IsPresent) { 'Error count: {0}' -f $Error.Count $Error | Out-String } } -ArgumentList @( $pesterConfig $ShowError.IsPresent $importedPesterModule.Version $BuildScriptPath $BuildScriptParameter ) | Receive-Job -AutoRemoveJob -Wait } #EndRegion '.\Public\Invoke-PesterJob.ps1' 528 #Region '.\Public\New-SamplerGitHubReleaseTag.ps1' -1 <# .SYNOPSIS Creates a new GitHub release tag for the Sampler project. .DESCRIPTION The New-SamplerGitHubReleaseTag function creates a new release tag for the Sampler project on GitHub. It performs the following steps: 1. Checks if the remote specified in $UpstreamRemoteName exists locally and throws an error if it doesn't. 2. Fetches the $DefaultBranchName branch from the $UpstreamRemoteName remote and throws an error if it doesn't exist. 3. Checks out the $DefaultBranchName branch. 4. Fetches the $DefaultBranchName branch from the $UpstreamRemoteName remote. 5. Rebases the local $DefaultBranchName branch with the $UpstreamRemoteName/$DefaultBranchName branch. 6. Gets the last commit ID of the $DefaultBranchName branch. 7. Fetches tags from the $UpstreamRemoteName remote. 8. If no release tag is specified, it checks if there are any tags in the local repository and selects the latest preview tag. 9. Creates a new tag with the specified release tag or based on the latest preview tag. 10. Optionally pushes the tag to the $UpstreamRemoteName remote. 11. Switches back to the previous branch if requested. .PARAMETER DefaultBranchName Specifies the name of the default branch. Default value is 'main'. .PARAMETER UpstreamRemoteName Specifies the name of the upstream remote. Default value is 'origin'. .PARAMETER ReleaseTag Specifies the release tag to create. Must be in the format 'vX.X.X'. If not specified, the latest preview tag will be used. .PARAMETER SwitchBackToPreviousBranch Specifies that the command should switches back to the previous branch after creating the release tag. .PARAMETER Force Specifies that the command should run without prompting for confirmation. .PARAMETER PushTag Specifies that the tag should also be pushed to the upstream remote after creating it. This will always ask for confirmation before pushing the tag, unless Force is also specified. .EXAMPLE New-SamplerGitHubReleaseTag -ReleaseTag 'v1.0.0' -PushTag Creates a new release tag with the specified tag 'v1.0.0' and pushes it to the 'origin' remote. .EXAMPLE New-SamplerGitHubReleaseTag -SwitchBackToPreviousBranch Creates a new release tag and switches back to the previous branch. .NOTES This function requires Git to be installed and accessible from the command line. #> function New-SamplerGitHubReleaseTag { [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [Parameter()] [System.String] $DefaultBranchName = 'main', [Parameter()] [System.String] $UpstreamRemoteName = 'origin', [Parameter()] [System.String] [ValidatePattern('^v\d+\.\d+\.\d+$')] $ReleaseTag, [Parameter()] [System.Management.Automation.SwitchParameter] $SwitchBackToPreviousBranch, [Parameter()] [System.Management.Automation.SwitchParameter] $Force, [Parameter()] [System.Management.Automation.SwitchParameter] $PushTag ) if ($Force.IsPresent -and -not $Confirm) { $ConfirmPreference = 'None' } # Check if the remote specified in $UpstreamRemoteName exists locally and throw an error if it doesn't. $remoteExists = git remote | Where-Object -FilterScript { $_ -eq $UpstreamRemoteName } if (-not $remoteExists) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ($script:localizedData.New_SamplerGitHubReleaseTag_RemoteMissing -f $UpstreamRemoteName), 'NSGRT0001', # cspell: disable-line [System.Management.Automation.ErrorCategory]::ObjectNotFound, $DatabaseName ) ) } $verboseDescriptionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessVerboseDescription -f $DefaultBranchName, $UpstreamRemoteName $verboseWarningMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessVerboseWarning -f $DefaultBranchName, $UpstreamRemoteName $captionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FetchUpstream_ShouldProcessCaption if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { # Fetch $DefaultBranchName from upstream and throw an error if it doesn't exist. git fetch $UpstreamRemoteName $DefaultBranchName if ($LASTEXITCODE -ne 0) # cSpell: ignore LASTEXITCODE { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ($script:localizedData.New_SamplerGitHubReleaseTag_FailedFetchBranchFromRemote -f $DefaultBranchName, $UpstreamRemoteName), 'NSGRT0002', # cspell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $DatabaseName ) ) } } if ($SwitchBackToPreviousBranch.IsPresent) { $currentLocalBranchName = git rev-parse --abbrev-ref HEAD if ($LASTEXITCODE -ne 0) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( $script:localizedData.New_SamplerGitHubReleaseTag_FailedGetLocalBranchName, 'NSGRT0003', # cspell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $DatabaseName ) ) } } $continueProcessing = $true $errorMessage = $null $verboseDescriptionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_Rebase_ShouldProcessVerboseDescription -f $DefaultBranchName, $UpstreamRemoteName $verboseWarningMessage = $script:localizedData.New_SamplerGitHubReleaseTag_Rebase_ShouldProcessVerboseWarning -f $DefaultBranchName $captionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_Rebase_ShouldProcessCaption if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { git checkout $DefaultBranchName if ($LASTEXITCODE -ne 0) { $continueProcessing = $false $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedCheckoutLocalBranch -f $DefaultBranchName $errorCode = 'NSGRT0004' # cspell: disable-line } $switchedToDefaultBranch = $true if ($continueProcessing) { git rebase $UpstreamRemoteName/$DefaultBranchName if ($LASTEXITCODE -ne 0) { $continueProcessing = $false $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedRebaseLocalDefaultBranch -f $DefaultBranchName, $UpstreamRemoteName $errorCode = 'NSGRT0005' # cspell: disable-line } if ($continueProcessing) { $headCommitId = git rev-parse HEAD if ($LASTEXITCODE -ne 0) { $continueProcessing = $false $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedGetLastCommitId -f $DefaultBranchName $errorCode = 'NSGRT0006' # cspell: disable-line } } } if (-not $continueProcessing) { # If something failed, revert back to the previous branch if requested. if ($SwitchBackToPreviousBranch.IsPresent -and $switchedToDefaultBranch) { git checkout $currentLocalBranchName } $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( $errorMessage, $errorCode, # cspell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $DatabaseName ) ) } } $verboseDescriptionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_UpstreamTags_ShouldProcessVerboseDescription -f $UpstreamRemoteName $verboseWarningMessage = $script:localizedData.New_SamplerGitHubReleaseTag_UpstreamTags_ShouldProcessVerboseWarning -f $UpstreamRemoteName $captionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_UpstreamTags_ShouldProcessCaption if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { git fetch $UpstreamRemoteName --tags if ($LASTEXITCODE -ne 0) { if ($SwitchBackToPreviousBranch.IsPresent -and $switchedToDefaultBranch) { git checkout $currentLocalBranchName } $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ($script:localizedData.New_SamplerGitHubReleaseTag_FailedFetchTagsFromUpstreamRemote -f $UpstreamRemoteName), 'NSGRT0007', # cspell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $DatabaseName ) ) } } if (-not $ReleaseTag) { $tagExist = git tag | Select-Object -First 1 if ($LASTEXITCODE -ne 0 -or -not $tagExist) { $continueProcessing = $false $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedGetTagsOrMissingTagsInLocalRepository $errorCode = 'NSGRT0008' # cspell: disable-line } if ($continueProcessing) { $latestPreviewTag = git describe --tags --abbrev=0 if ($LASTEXITCODE -ne 0) { $continueProcessing = $false $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_FailedDescribeTags $errorCode = 'NSGRT0009' # cspell: disable-line } if ($continueProcessing) { $isCorrectlyFormattedPreviewTag = $latestPreviewTag -match '^(v\d+\.\d+\.\d+)-.*' if ($isCorrectlyFormattedPreviewTag) { $ReleaseTag = $matches[1] } else { $continueProcessing = $false $errorMessage = $script:localizedData.New_SamplerGitHubReleaseTag_LatestTagIsNotPreview -f $latestPreviewTag $errorCode = 'NSGRT0010' # cspell: disable-line } } } if (-not $continueProcessing) { if ($SwitchBackToPreviousBranch.IsPresent -and $switchedToDefaultBranch) { git checkout $currentLocalBranchName } $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( $errorMessage, $errorCode, # cspell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $DatabaseName ) ) } } if ($WhatIfPreference) { $messageShouldProcess = $script:localizedData.New_SamplerGitHubReleaseTag_NewTagWhatIf_ShouldProcessVerboseDescription } else { $messageShouldProcess = $script:localizedData.New_SamplerGitHubReleaseTag_NewTag_ShouldProcessVerboseDescription } $verboseDescriptionMessage = $messageShouldProcess -f $ReleaseTag, $DefaultBranchName, $headCommitId $verboseWarningMessage = $script:localizedData.New_SamplerGitHubReleaseTag_NewTag_ShouldProcessVerboseWarning -f $ReleaseTag $captionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_NewTag_ShouldProcessCaption if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { git tag $ReleaseTag if ($PushTag -and ($Force -or $PSCmdlet.ShouldContinue(('Do you want to push the tags to the upstream ''{0}''?' -f $UpstreamRemoteName), 'Confirm'))) { git push origin --tags Write-Information -MessageData ("`e[32mTag `e[1;37;44m{0}`e[0m`e[32m was created and pushed to upstream '{1}'`e[0m" -f $ReleaseTag, $UpstreamRemoteName) -InformationAction Continue } else { # cSpell: disable-next-line Write-Information -MessageData ("`e[32mTag `e[1;37;44m{0}`e[0m`e[32m was created. To push the tag to upstream, run `e[1;37;44mgit push {1} --tags`e[0m`e[32m.`e[0m" -f $ReleaseTag, $UpstreamRemoteName) -InformationAction Continue } } if ($SwitchBackToPreviousBranch.IsPresent) { $verboseDescriptionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_SwitchBack_ShouldProcessVerboseDescription -f $currentLocalBranchName $verboseWarningMessage = $script:localizedData.New_SamplerGitHubReleaseTag_SwitchBack_ShouldProcessVerboseWarning -f $currentLocalBranchName $captionMessage = $script:localizedData.New_SamplerGitHubReleaseTag_SwitchBack_ShouldProcessCaption if ($PSCmdlet.ShouldProcess($verboseDescriptionMessage, $verboseWarningMessage, $captionMessage)) { git checkout $currentLocalBranchName if ($LASTEXITCODE -ne 0) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( ($script:localizedData.New_SamplerGitHubReleaseTag_FailedCheckoutPreviousBranch -f $currentLocalBranchName), 'NSGRT0011', # cspell: disable-line [System.Management.Automation.ErrorCategory]::InvalidOperation, $DatabaseName ) ) } } } } #EndRegion '.\Public\New-SamplerGitHubReleaseTag.ps1' 348 #Region '.\Public\Out-Difference.ps1' -1 <# .SYNOPSIS Compares two sets of strings and converts them into a difference string. .DESCRIPTION The Out-Difference function compares two sets of strings, Reference and Difference, and converts them into a difference string. It provides options to customize the indicators, labels, and formatting of the output. .PARAMETER Reference Specifies the reference set of strings to compare. .PARAMETER Difference Specifies the difference set of strings to compare. .PARAMETER EqualIndicator Specifies the indicator to use for equal strings. .PARAMETER NotEqualIndicator Specifies the indicator to use for unequal strings. .PARAMETER HighlightStart Specifies the starting indicator for highlighting differences. .PARAMETER HighlightEnd Specifies the ending indicator for highlighting differences. .PARAMETER ReferenceLabel Specifies the label for the reference set. .PARAMETER DifferenceLabel Specifies the label for the difference set. .PARAMETER NoColumnHeader Indicates whether to exclude the column header from the output. .PARAMETER NoLabels Indicates whether to exclude the labels from the output. .PARAMETER ReferenceLabelAnsi Specifies the ANSI escape sequence for the reference label. .PARAMETER DifferenceLabelAnsi Specifies the ANSI escape sequence for the difference label. .PARAMETER ColumnHeaderAnsi Specifies the ANSI escape sequence for the column header. .PARAMETER ColumnHeaderResetAnsi Specifies the ANSI escape sequence to reset the column header formatting. .PARAMETER EncodingType Specifies the encoding type to use for converting the strings to byte arrays. .PARAMETER ConcatenateArray Indicates whether to concatenate the arrays of strings into a single string. .PARAMETER ConcatenateChar Specifies the character used to concatenate the strings. Default is a new line character. .EXAMPLE $reference = "apple", "banana", "cherry" $difference = "apple", "orange", "cherry" Out-Difference -Reference $reference -Difference $difference -EqualIndicator '' -ReferenceLabel 'Reference:' -DifferenceLabel 'Difference:' -ConcatenateArray -ConcatenateChar '' .INPUTS None. You cannot pipe input to this function. .OUTPUTS System.String. The difference string representing the comparison between the reference and difference sets. .NOTES This command is using the default parameters values from the ConvertTo-DifferenceString command. #> function Out-Difference { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [AllowEmptyString()] [AllowNull()] [AllowEmptyCollection()] [System.String[]] $Reference, [Parameter(Mandatory = $true)] [AllowEmptyString()] [AllowNull()] [AllowEmptyCollection()] [System.String[]] $Difference, [Parameter()] [ValidateLength(0, 2)] [System.String] $EqualIndicator, [Parameter()] [ValidateLength(0, 2)] [System.String] $NotEqualIndicator, [Parameter()] [System.String] $HighlightStart, [Parameter()] [System.String] $HighlightEnd, [Parameter()] [System.String] $ReferenceLabel, [Parameter()] [System.String] $DifferenceLabel, [Parameter()] [System.Management.Automation.SwitchParameter] $NoColumnHeader, [Parameter()] [System.Management.Automation.SwitchParameter] $NoLabels, [Parameter()] [System.String] $ReferenceLabelAnsi, [Parameter()] [System.String] $DifferenceLabelAnsi, [Parameter()] [System.String] $ColumnHeaderAnsi, [Parameter()] [System.String] $ColumnHeaderResetAnsi, [Parameter()] [ValidateSet('ASCII', 'BigEndianUnicode', 'Default', 'Unicode', 'UTF32', 'UTF7', 'UTF8')] [System.String] $EncodingType, [Parameter()] [System.Management.Automation.SwitchParameter] $ConcatenateArray, [Parameter()] [System.String] $ConcatenateChar = [System.Environment]::NewLine ) if ($null -eq $ConcatenateChar) { $ConcatenateChar = '' } $behaviorParameters = @{} + $PSBoundParameters $behaviorParameters.Remove('Reference') $behaviorParameters.Remove('Difference') $behaviorParameters.Remove('ConcatenateArray') $behaviorParameters.Remove('ConcatenateChar') if ($ConcatenateArray.IsPresent) { # Handle null values by converting them to empty strings if ($null -eq $Reference) { $refString = '' } else { $refString = $Reference -join $ConcatenateChar } if ($null -eq $Difference) { $diffString = '' } else { $diffString = $Difference -join $ConcatenateChar } ConvertTo-DifferenceString -ReferenceString $refString -DifferenceString $diffString @behaviorParameters } else { for ($i = 0; $i -lt [Math]::Max($Reference.Length, $Difference.Length); $i++) { $refString = if ($i -lt $Reference.Length) { $Reference[$i] } else { '' } $diffString = if ($i -lt $Difference.Length) { $Difference[$i] } else { '' } ConvertTo-DifferenceString -ReferenceString $refString -DifferenceString $diffString @behaviorParameters } } } #EndRegion '.\Public\Out-Difference.ps1' 220 #Region '.\Public\Pop-VMLatestSnapshot.ps1' -1 <# .SYNOPSIS Sets the latest snapshot of a virtual machine and starts it. .DESCRIPTION The Pop-VMLatestSnapShot command sets the latest snapshot of a virtual machine specified by the $ServerName parameter and starts it. .PARAMETER ServerName Specifies the name of the server for which to set the latest snapshot. .EXAMPLE Pop-VMLatestSnapShot -ServerName 'VM1' Sets the latest snapshot of the virtual machine named "VM1" and starts it. #> function Pop-VMLatestSnapShot { param ( [Parameter(Mandatory = $true)] [System.String] $ServerName ) Get-VM -Name $ServerName | Get-Snapshot | # TODO: Should this not be Get-VMSnapshot? Where-Object -FilterScript { $_.IsCurrent -eq $true } | Set-VM -VM $ServerName | # TODO: Is -VM necessary? Start-VM } #EndRegion '.\Public\Pop-VMLatestSnapshot.ps1' 34 #Region '.\Public\Remove-History.ps1' -1 <# .SYNOPSIS Removes command history entries that match a specified pattern. .DESCRIPTION The Remove-History function removes command history entries that match a specified pattern. It removes both the history entries stored by the PSReadLine module and the history entries stored by the PowerShell session. .PARAMETER Pattern Specifies the pattern to match against the command history entries. Only the entries that match the pattern will be removed. .PARAMETER EscapeRegularExpression Indicates that the pattern should be treated as a literal string. If this switch parameter is specified, the pattern will not be treated as a regular expression. .INPUTS None. You cannot pipe input to this function. .OUTPUTS None. The function does not generate any output. .EXAMPLE Remove-History -Pattern ".*\.txt" This example removes all command history entries that end with the ".txt" extension, using a regular expression pattern. .EXAMPLE Remove-History -Pattern './build.ps1' -EscapeRegularExpression This example removes all command history entries that contain the string "./build.ps1". #> function Remove-History { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '', Justification = 'Because ShouldProcess is handled in the commands it calls')] [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true, Position = 0)] [System.String] $Pattern, [Parameter()] [System.Management.Automation.SwitchParameter] $EscapeRegularExpression ) Remove-PSReadLineHistory @PSBoundParameters Remove-PSHistory @PSBoundParameters } #EndRegion '.\Public\Remove-History.ps1' 55 #Region '.\Public\Remove-PSHistory.ps1' -1 <# .SYNOPSIS Removes PowerShell history content matching a specified pattern. .DESCRIPTION The Remove-PSHistory function removes PowerShell history content that matches a specified pattern. .PARAMETER Pattern Specifies the pattern to match against the command history entries. Only the entries that match the pattern will be removed. .PARAMETER EscapeRegularExpression Indicates that the pattern should be treated as a literal string. If this switch parameter is specified, the pattern will not be treated as a regular expression. .EXAMPLE Remove-PSHistory -Pattern ".*\.txt" This example removes all command history entries that end with the ".txt" extension, using a regular expression pattern. .EXAMPLE Remove-PSHistory -Pattern './build.ps1' -EscapeRegularExpression This example removes all command history entries that contain the string "./build.ps1". .INPUTS None. You cannot pipe input to this function. .OUTPUTS None. The function does not generate any output. #> function Remove-PSHistory { [CmdletBinding(SupportsShouldProcess = $true , ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true, Position = 0)] [System.String] $Pattern, [Parameter()] [System.Management.Automation.SwitchParameter] $EscapeRegularExpression ) if ($EscapeRegularExpression.IsPresent) { $Pattern = [System.Text.RegularExpressions.Regex]::Escape($Pattern) } $historyContent = Get-History $matchingLines = $historyContent | Where-Object -FilterScript { $_.CommandLine -match $Pattern } if ($matchingLines) { $matchingLines | Write-Verbose -Verbose $shouldProcessVerboseDescription = 'Removing content matching the pattern ''{0}''.' -f $Pattern $shouldProcessVerboseWarning = 'Are you sure you want to remove the content matching the pattern ''{0}'' from PowerShell history?' -f $Pattern # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. $shouldProcessCaption = 'Remove content matching the pattern from PowerShell history' if ($PSCmdlet.ShouldProcess($shouldProcessVerboseDescription, $shouldProcessVerboseWarning, $shouldProcessCaption)) { $matchingLines | ForEach-Object -Process { Clear-History -Id $_.Id } Write-Information -MessageData 'Removed PowerShell history content matching the pattern.' -InformationAction Continue } } else { Write-Information -MessageData 'No PowerShell history content matching the pattern.' -InformationAction Continue } } #EndRegion '.\Public\Remove-PSHistory.ps1' 86 #Region '.\Public\Remove-PSReadLineHistory.ps1' -1 <# .SYNOPSIS Removes content from the PSReadLine history that matches a specified pattern. .DESCRIPTION The Remove-PSReadLineHistory function removes content from the PSReadLine history that matches a specified pattern. .PARAMETER Pattern Specifies the pattern to match against the command history entries. Only the entries that match the pattern will be removed. .PARAMETER EscapeRegularExpression Indicates that the pattern should be treated as a literal string. If this switch parameter is specified, the pattern will not be treated as a regular expression. .NOTES - This command requires the PSReadLine module to be installed. - The PSReadLine history is stored in a file specified by the HistorySavePath property of the PSReadLineOption object. .EXAMPLE Remove-PSReadLineHistory -Pattern ".*\.txt" This example removes all command history entries that end with the ".txt" extension, using a regular expression pattern. .EXAMPLE Remove-PSReadLineHistory -Pattern './build.ps1' -EscapeRegularExpression This example removes all command history entries that contain the string "./build.ps1". .INPUTS None. You cannot pipe input to this function. .OUTPUTS None. The function does not generate any output. #> function Remove-PSReadLineHistory { [CmdletBinding(SupportsShouldProcess = $true , ConfirmImpact = 'High')] param ( [Parameter(Mandatory = $true, Position = 0)] [System.String] $Pattern, [Parameter()] [System.Management.Automation.SwitchParameter] $EscapeRegularExpression ) if ($EscapeRegularExpression.IsPresent) { $Pattern = [System.Text.RegularExpressions.Regex]::Escape($Pattern) } $historyPath = (Get-PSReadLineOption).HistorySavePath $historyContent = Get-Content -Path $historyPath # Do not match the last line as it is the line that called the function. $matchingContent = $historyContent | Select-Object -SkipLast 1 | Select-String -Pattern $Pattern if ($matchingContent) { $matchingContent | Write-Verbose -Verbose $shouldProcessVerboseDescription = 'Removing content matching the pattern ''{0}''.' -f $Pattern $shouldProcessVerboseWarning = 'Are you sure you want to remove the content matching the pattern ''{0}'' from PSReadLine history?' -f $Pattern # This string shall not end with full stop (.) since it is used as a title of ShouldProcess messages. $shouldProcessCaption = 'Remove content matching the pattern from PSReadLine history' if ($PSCmdlet.ShouldProcess($shouldProcessVerboseDescription, $shouldProcessVerboseWarning, $shouldProcessCaption)) { Set-Content -Path $historyPath -Value ( $historyContent | Select-String -NotMatch $Pattern ).Line Write-Information -MessageData 'Removed PSReadLine history content matching the pattern.' -InformationAction Continue } } else { Write-Information -MessageData 'No PSReadLine history content matching the pattern.' -InformationAction Continue } } #EndRegion '.\Public\Remove-PSReadLineHistory.ps1' 94 #Region '.\Public\Split-StringAtIndex.ps1' -1 <# .SYNOPSIS Splits a string at a specified index or range of indices. .DESCRIPTION The Split-StringAtIndex function splits a given string at a specified index or range of indices. It can be used to extract substrings from a larger string based on the provided indices. .PARAMETER IndexObject Specifies the index object to split the string. This parameter is used when providing input via the pipeline. .PARAMETER InputString Specifies the input string to be split. .PARAMETER StartIndex Specifies the starting index of the substring to be extracted. The value must be less than the length of the input string. .PARAMETER EndIndex Specifies the ending index of the substring to be extracted. The value must be less than the length of the input string. .EXAMPLE PS> Split-StringAtIndex -InputString "Hello, World!" -StartIndex 0 -EndIndex 4 This example splits the input string "Hello, World!" at the index specified by StartIndex and then at the index specified by EndIndex and returns the resulting array of substrings. .EXAMPLE PS> @(@{Start = 0; End = 2}, @{Start = 7; End = 11 }) | Split-StringAtIndex -InputString "Hello, world!" This example splits the input string "Hello, World!" at the indices provided by the pipeline. It will split the string at each StartIndex and EndIndex and returns the resulting array of substrings. .EXAMPLE PS> @(0, 1, 2, 7, 8, 9, 10, 11) | Get-NumericalSequence | Split-StringAtIndex -InputString "Hello, world!" This example splits the input string "Hello, World!" at the indices provided by the pipeline. It will split the string at each StartIndex and EndIndex and returns the resulting array of substrings. .OUTPUTS System.String[] An array of substrings extracted from the input string. .NOTES The Split-StringAtIndex function is designed to split strings based on indices and can be used in various scenarios where string manipulation is required. To get the indices the function Get-NumericalSequence can be used. #> function Split-StringAtIndex { [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('UseSyntacticallyCorrectExamples', '', Justification = 'Because the examples are syntactically correct. The rule does not seem to understand that there is pipeline input.')] [CmdletBinding()] [OutputType([System.String])] param ( [Parameter(ParameterSetName = 'PipelineInput', Mandatory = $true, ValueFromPipeline = $true)] [PSCustomObject] $IndexObject, [Parameter(ParameterSetName = 'StartEndInput', Mandatory = $true)] [Parameter(ParameterSetName = 'PipelineInput', Mandatory = $true)] [System.String] $InputString, [Parameter(ParameterSetName = 'StartEndInput', Mandatory = $true)] [ValidateScript({ $_ -lt $InputString.Length })] [System.UInt32] $StartIndex, [Parameter(ParameterSetName = 'StartEndInput', Mandatory = $true)] [ValidateScript({ $_ -lt $InputString.Length })] [System.UInt32] $EndIndex ) begin { $result = @() $previousIndex = 0 } process { switch ($PSCmdlet.ParameterSetName) { 'PipelineInput' { $start = $IndexObject.Start $end = $IndexObject.End } 'StartEndInput' { $start = $StartIndex $end = $EndIndex } } if ($null -eq $end) { $end = $start } if ($start -gt $previousIndex) { $result += $InputString.Substring($previousIndex, $start - $previousIndex) } $result += $InputString.Substring($start, $end - $start + 1) $previousIndex = $end + 1 } end { if ($previousIndex -lt $InputString.Length) { $result += $InputString.Substring($previousIndex) } $result } } #EndRegion '.\Public\Split-StringAtIndex.ps1' 131 |