ToolFunctions.ps1
<#
.SYNOPSIS Asserts the agent version is at least the specified minimum. .PARAMETER Minimum Minimum version - must be 2.104.1 or higher. #> function Assert-Agent { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [version]$Minimum) if (([version]'2.104.1').CompareTo($Minimum) -ge 1) { Write-Error "Assert-Agent requires the parameter to be 2.104.1 or higher." return } $agent = Get-TaskVariable -Name 'agent.version' if (!$agent -or $Minimum.CompareTo([version]$agent) -ge 1) { Write-Error (Get-LocString -Key 'PSLIB_AgentVersion0Required' -ArgumentList $Minimum) } } <# .SYNOPSIS Asserts that a path exists. Throws if the path does not exist. .PARAMETER PassThru True to return the path. #> function Assert-Path { [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$LiteralPath, [Microsoft.PowerShell.Commands.TestPathType]$PathType = [Microsoft.PowerShell.Commands.TestPathType]::Any, [switch]$PassThru) if ($PathType -eq [Microsoft.PowerShell.Commands.TestPathType]::Any) { Write-Verbose "Asserting path exists: '$LiteralPath'" } else { Write-Verbose "Asserting $("$PathType".ToLowerInvariant()) path exists: '$LiteralPath'" } if (Test-Path -LiteralPath $LiteralPath -PathType $PathType) { if ($PassThru) { return $LiteralPath } return } $resourceKey = switch ($PathType) { ([Microsoft.PowerShell.Commands.TestPathType]::Container) { "PSLIB_ContainerPathNotFound0" ; break } ([Microsoft.PowerShell.Commands.TestPathType]::Leaf) { "PSLIB_LeafPathNotFound0" ; break } default { "PSLIB_PathNotFound0" } } throw (Get-LocString -Key $resourceKey -ArgumentList $LiteralPath) } <# .SYNOPSIS Executes an external program. .DESCRIPTION Executes an external program and waits for the process to exit. After calling this command, the exit code of the process can be retrieved from the variable $LASTEXITCODE. .PARAMETER Encoding This parameter not required for most scenarios. Indicates how to interpret the encoding from the external program. An example use case would be if an external program outputs UTF-16 XML and the output needs to be parsed. .PARAMETER RequireExitCodeZero Indicates whether to write an error to the error pipeline if the exit code is not zero. #> function Invoke-Tool { [CmdletBinding()] param( [ValidatePattern('^[^\r\n]*$')] [Parameter(Mandatory = $true)] [string]$FileName, [ValidatePattern('^[^\r\n]*$')] [Parameter()] [string]$Arguments, [string]$WorkingDirectory, [System.Text.Encoding]$Encoding, [switch]$RequireExitCodeZero, [bool]$IgnoreHostException) Trace-EnteringInvocation $MyInvocation $isPushed = $false $originalEncoding = $null try { if ($Encoding) { $originalEncoding = [System.Console]::OutputEncoding [System.Console]::OutputEncoding = $Encoding } if ($WorkingDirectory) { Push-Location -LiteralPath $WorkingDirectory -ErrorAction Stop $isPushed = $true } $FileName = $FileName.Replace('"', '').Replace("'", "''") Write-Host "##[command]""$FileName"" $Arguments" try { Invoke-Expression "& '$FileName' --% $Arguments" } catch [System.Management.Automation.Host.HostException] { if ($IgnoreHostException -eq $False) { throw } Write-Host "##[warning]Host Exception was thrown by Invoke-Expression, suppress it due IgnoreHostException setting" } Write-Verbose "Exit code: $LASTEXITCODE" if ($RequireExitCodeZero -and $LASTEXITCODE -ne 0) { Write-Error (Get-LocString -Key PSLIB_Process0ExitedWithCode1 -ArgumentList ([System.IO.Path]::GetFileName($FileName)), $LASTEXITCODE) } } finally { if ($originalEncoding) { [System.Console]::OutputEncoding = $originalEncoding } if ($isPushed) { Pop-Location } Trace-LeavingInvocation $MyInvocation } } <# .SYNOPSIS Executes an external program as a child process. .DESCRIPTION Executes an external program and waits for the process to exit. After calling this command, the exit code of the process can be retrieved from the variable $LASTEXITCODE or from the pipe. .PARAMETER FileName File name (path) of the program to execute. .PARAMETER Arguments Arguments to pass to the program. .PARAMETER StdOutPath Path to a file to write the stdout of the process to. .PARAMETER StdErrPath Path to a file to write the stderr of the process to. .PARAMETER RequireExitCodeZero Indicates whether to write an error to the error pipeline if the exit code is not zero. .OUTPUTS Exit code of the invoked process. Also available through the $LASTEXITCODE. .NOTES To change output encoding, redirect stdout to file and then read the file with the desired encoding. #> function Invoke-Process { [CmdletBinding()] param( [ValidatePattern('^[^\r\n]*$')] [Parameter(Mandatory = $true)] [string]$FileName, [ValidatePattern('^[^\r\n]*$')] [Parameter()] [string]$Arguments, [string]$WorkingDirectory, [string]$StdOutPath, [string]$StdErrPath, [switch]$RequireExitCodeZero ) Trace-EnteringInvocation $MyInvocation try { $FileName = $FileName.Replace('"', '').Replace("'", "''") Write-Host "##[command]""$FileName"" $Arguments" $processOptions = @{ FilePath = $FileName NoNewWindow = $true PassThru = $true } if ($Arguments) { $processOptions.Add("ArgumentList", $Arguments) } if ($WorkingDirectory) { $processOptions.Add("WorkingDirectory", $WorkingDirectory) } if ($StdOutPath) { $processOptions.Add("RedirectStandardOutput", $StdOutPath) } if ($StdErrPath) { $processOptions.Add("RedirectStandardError", $StdErrPath) } # TODO: For some reason, -Wait is not working on agent. # Agent starts executing the System usage metrics and hangs the step forever. $proc = Start-Process @processOptions # https://stackoverflow.com/a/23797762 $null = $($proc.Handle) $proc.WaitForExit() $procExitCode = $proc.ExitCode Write-Verbose "Exit code: $procExitCode" if ($RequireExitCodeZero -and $procExitCode -ne 0) { Write-Error (Get-LocString -Key PSLIB_Process0ExitedWithCode1 -ArgumentList ([System.IO.Path]::GetFileName($FileName)), $procExitCode) } $global:LASTEXITCODE = $procExitCode return $procExitCode } finally { Trace-LeavingInvocation $MyInvocation } } |