_Private/_Private.ps1
function _GetAbsolutePath { [CmdletBinding()] Param ( $Path, [switch]$CreateFolder ) Process { $Path = [System.IO.Path]::Combine($pwd.Path, $Path) $Path = [System.IO.Path]::GetFullPath($Path) $folder = Split-Path $Path -Parent if ((-not (Test-Path $folder)) -and ($CreateFolder.IsPresent)) { New-Item $folder -ItemType Directory -Force | _Log } return $Path } } Function _GetJsonPatchDocument { [CmdletBinding()] [OutputType([Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchDocument])] Param ( $operations ) $doc = New-Object 'Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchDocument' foreach($op in $operations) { if ($op -is [Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchOperation]) { $doc.Add($op) continue } $jsonOp = New-Object 'Microsoft.VisualStudio.Services.WebApi.Patch.Json.JsonPatchOperation' $jsonOp.Operation = $op.Operation $jsonOp.Path = $op.Path $jsonOp.Value = $op.Value $doc.Add($jsonOp) } Write-Output -NoEnumerate $doc } Function _GetRegistryValue { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Value ) Process { return Get-ItemProperty -Path $Path | Select-Object -ExpandProperty $Value } } Function _GetRestClient { [CmdletBinding()] [OutputType('Microsoft.VisualStudio.Services.WebApi.VssHttpClientBase')] Param ( [Parameter(Mandatory=$true, Position=0)] [string] $Type, [Parameter()] [object] $Collection ) Process { $tpc = Get-TfsTeamProjectCollection -Collection $Collection return _InvokeGenericMethod -InputObject $tpc -MethodName GetClient -GenericType $Type } } Function _GetStructureGroup($StructureGroup) { Write-Warning $MyInvocation.InvocationName if($StructureGroup -is [Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.TreeStructureGroup]) { _Log "StructureGroup already set to '$StructureGroup'; returning" return $StructureGroup } _Log "Getting structure group from call stack" foreach($frame in Get-PSCallStack) { _Log "Command '$($frame.Command)'" if ($frame.Command.EndsWith('Area')) { return [Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.TreeStructureGroup]::Areas } elseif ($frame.Command.EndsWith('Iteration')) { return [Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.TreeStructureGroup]::Iterations } } throw "Invalid or missing StructureGroup argument" } Function _ImportRequiredAssembly($assemblyName) { $libPath = (Join-Path $PSScriptRoot "../lib" -Resolve) if($assemblyName -eq '*') { $assemblies = (Get-ChildItem "$libPath/*.dll" -Exclude 'Microsoft.WitDataStore*.*','TfsCmdletsLib.dll').BaseName $assemblies += (Get-ChildItem "$libPath/TfsCmdletsLib.dll").BaseName } else { $assemblies = (Get-ChildItem "$libPath/$assemblyName.dll").BaseName } foreach($asm in $assemblies) { Write-Verbose "Loading assembly $asm from folder $libPath" try { Add-Type -Path (Join-Path $libPath "$asm.dll") } catch { Write-Warning "Error loading assembly '$asm': $($_.Exception.Message)" } } } Function _TestLoadedAssembly($assemblyName) { try { $asm = [System.AppDomain]::CurrentDomain.GetAssemblies() | Where-Object FullName -like "$assemblyName,*" return $asm -is [System.Reflection.Assembly] } catch { return $false } } function _InvokeGenericMethod { [CmdletBinding(DefaultParameterSetName = 'Instance')] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, ParameterSetName = 'Instance')] $InputObject, [Parameter(Mandatory = $true, ParameterSetName = 'Static')] [Type] $Type, [Parameter(Mandatory = $true)] [string] $MethodName, [Parameter(Mandatory = $true)] [Type[]] $GenericType, [Object[]] $ArgumentList ) process { switch ($PSCmdlet.ParameterSetName) { 'Instance' { $_type = $InputObject.GetType() $object = $InputObject $flags = [System.Reflection.BindingFlags] 'Instance, Public' } 'Static' { $_type = $Type $object = $null $flags = [System.Reflection.BindingFlags] 'Static, Public' } } if ($null -ne $ArgumentList) { $argList = $ArgumentList.Clone() } else { $argList = @() } $params = @{ Type = $_type BindingFlags = $flags MethodName = $MethodName GenericType = $GenericType ArgumentList = [ref]$argList } $method = _GetGenericMethod @params if ($null -eq $method) { Write-Error "No matching method was found" return } return [PSGenericMethods.MethodInvoker]::InvokeMethod($method, $object, $argList) } } function _GetGenericMethod { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [Type] $Type, [Parameter(Mandatory = $true)] [string] $MethodName, [Parameter(Mandatory = $true)] [Type[]] $GenericType, [ref] $ArgumentList, [System.Reflection.BindingFlags] $BindingFlags = [System.Reflection.BindingFlags]::Default, [switch] $WithCoercion ) if ($null -eq $ArgumentList.Value) { $originalArgList = @() } else { $originalArgList = @($ArgumentList.Value) } foreach ($method in $Type.GetMethods($BindingFlags)) { $argList = $originalArgList.Clone() if (-not $method.IsGenericMethod -or $method.Name -ne $MethodName) { continue } if ($GenericType.Count -ne $method.GetGenericArguments().Count) { continue } if (_TestGenericMethodParameters -MethodInfo $method -ArgumentList ([ref]$argList) -GenericType $GenericType -WithCoercion:$WithCoercion) { $ArgumentList.Value = $argList return $method.MakeGenericMethod($GenericType) } } if (-not $WithCoercion) { $null = $PSBoundParameters.Remove('WithCoercion') return _GetGenericMethod @PSBoundParameters -WithCoercion } } function _TestGenericMethodParameters { [CmdletBinding()] param ( [System.Reflection.MethodInfo] $MethodInfo, [ref] $ArgumentList, [Parameter(Mandatory = $true)] [Type[]] $GenericType, [switch] $WithCoercion ) if ($null -eq $ArgumentList.Value) { $argList = @() } else { $argList = @($ArgumentList.Value) } $parameterList = $MethodInfo.GetParameters() $arrayType = $null $_HasParamsArray = _HasParamsArray -ParameterList $parameterList if ($parameterList.Count -lt $argList.Count -and -not $_HasParamsArray) { return $false } $methodGenericType = $MethodInfo.GetGenericArguments() for ($i = 0; $i -lt $argList.Count; $i++) { $params = @{ ArgumentList = $argList ParameterList = $ParameterList WithCoercion = $WithCoercion RuntimeGenericType = $GenericType MethodGenericType = $methodGenericType Index = [ref]$i ArrayType = [ref]$arrayType } $isOk = _TryMatchParameter @params if (-not $isOk) { return $false } } $defaults = New-Object System.Collections.ArrayList for ($i = $argList.Count; $i -lt $parameterList.Count; $i++) { if (-not $parameterList[$i].HasDefaultValue) { return $false } $null = $defaults.Add($parameterList[$i].DefaultValue) } # When calling a method with a params array using MethodInfo, you have to pass in the array; the # params argument approach doesn't work. if ($_HasParamsArray) { $firstArrayIndex = $parameterList.Count - 1 $lastArrayIndex = $argList.Count - 1 $newArgList = $argList[0..$firstArrayIndex] $newArgList[$firstArrayIndex] = $argList[$firstArrayIndex..$lastArrayIndex] -as $arrayType $argList = $newArgList } $ArgumentList.Value = $argList + $defaults.ToArray() return $true } function _TryMatchParameter { param ( [System.Reflection.ParameterInfo[]] $ParameterList, [object[]] $ArgumentList, [Type[]] $MethodGenericType, [Type[]] $RuntimeGenericType, [switch] $WithCoercion, [ref] $Index, [ref] $ArrayType ) $params = @{ ParameterType = $ParameterList[$Index.Value].ParameterType RuntimeType = $RuntimeGenericType GenericType = $MethodGenericType } $runtimeType = _ResolveRuntimeType @params if ($null -eq $runtimeType) { throw "Could not determine runtime type of parameter '$($ParameterList[$Index.Value].Name)'" } $_IsParamsArray = _IsParamsArray -ParameterInfo $ParameterList[$Index.Value] if ($_IsParamsArray) { $ArrayType.Value = $runtimeType $runtimeType = $runtimeType.GetElementType() } do { $isOk = _TryMatchArgument @PSBoundParameters -RuntimeType $runtimeType if (-not $isOk) { return $false } if ($_IsParamsArray) { $Index.Value++ } } while ($_IsParamsArray -and $Index.Value -lt $ArgumentList.Count) return $true } function _TryMatchArgument { param ( [System.Reflection.ParameterInfo[]] $ParameterList, [object[]] $ArgumentList, [Type[]] $MethodGenericType, [Type[]] $RuntimeGenericType, [switch] $WithCoercion, [ref] $Index, [ref] $ArrayType, [Type] $RuntimeType ) Function _GetType($object) { if ($null -eq $object) { return $null } return $object.GetType() } $argValue = $ArgumentList[$Index.Value] $argType = _GetType $argValue $isByRef = $RuntimeType.IsByRef if ($isByRef) { if ($ArgumentList[$Index.Value] -isnot [ref]) { return $false } $RuntimeType = $RuntimeType.GetElementType() $argValue = $argValue.Value $argType = _GetType $argValue } $isNullNullable = $false while ($RuntimeType.FullName -like 'System.Nullable``1*') { if ($null -eq $argValue) { $isNullNullable = $true break } $RuntimeType = $RuntimeType.GetGenericArguments()[0] } if ($isNullNullable) { continue } if ($null -eq $argValue) { return -not $RuntimeType.IsValueType } else { if ($argType -ne $RuntimeType) { $argValue = $argValue -as $RuntimeType if (-not $WithCoercion -or $null -eq $argValue) { return $false } } if ($isByRef) { $ArgumentList[$Index.Value].Value = $argValue } else { $ArgumentList[$Index.Value] = $argValue } } return $true } function _HasParamsArray([System.Reflection.ParameterInfo[]] $ParameterList) { return $ParameterList.Count -gt 0 -and (_IsParamsArray -ParameterInfo $ParameterList[-1]) } function _IsParamsArray([System.Reflection.ParameterInfo] $ParameterInfo) { return @($ParameterInfo.GetCustomAttributes([System.ParamArrayAttribute], $true)).Count -gt 0 } function _ResolveRuntimeType { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [Type] $ParameterType, [Parameter(Mandatory = $true)] [Type[]] $RuntimeType, [Parameter(Mandatory = $true)] [Type[]] $GenericType ) if ($ParameterType.IsByRef) { $elementType = _ResolveRuntimeType -ParameterType $ParameterType.GetElementType() -RuntimeType $RuntimeType -GenericType $GenericType return $elementType.MakeByRefType() } elseif ($ParameterType.IsGenericParameter) { for ($i = 0; $i -lt $GenericType.Count; $i++) { if ($ParameterType -eq $GenericType[$i]) { return $RuntimeType[$i] } } } elseif ($ParameterType.IsArray) { $arrayType = $ParameterType $elementType = _ResolveRuntimeType -ParameterType $ParameterType.GetElementType() -RuntimeType $RuntimeType -GenericType $GenericType if ($ParameterType.GetElementType().IsGenericParameter) { $arrayRank = $arrayType.GetArrayRank() if ($arrayRank -eq 1) { $arrayType = $elementType.MakeArrayType() } else { $arrayType = $elementType.MakeArrayType($arrayRank) } } return $arrayType } elseif ($ParameterType.ContainsGenericParameters) { $genericArguments = $ParameterType.GetGenericArguments() $runtimeArguments = New-Object System.Collections.ArrayList foreach ($argument in $genericArguments) { $null = $runtimeArguments.Add((_ResolveRuntimeType -ParameterType $argument -RuntimeType $RuntimeType -GenericType $GenericType)) } $definition = $ParameterType if (-not $definition.IsGenericTypeDefinition) { $definition = $definition.GetGenericTypeDefinition() } return $definition.MakeGenericType($runtimeArguments.ToArray()) } else { return $ParameterType } } if (-not ([System.Management.Automation.PSTypeName]'PSGenericMethods.MethodInvoker').Type) { Add-Type -ErrorAction SilentlyContinue -TypeDefinition @' namespace PSGenericMethods { using System; using System.Reflection; using System.Management.Automation; public static class MethodInvoker { public static object InvokeMethod(MethodInfo method, object target, object[] arguments) { if (method == null) { throw new ArgumentNullException("method"); } object[] args = null; if (arguments != null) { args = (object[])arguments.Clone(); for (int i = 0; i < args.Length; i++) { PSObject pso = args[i] as PSObject; if (pso != null) { args[i] = pso.BaseObject; } PSReference psref = args[i] as PSReference; if (psref != null) { args[i] = psref.Value; } } } object result = method.Invoke(target, args); for (int i = 0; i < arguments.Length; i++) { PSReference psref = arguments[i] as PSReference; if (psref != null) { psref.Value = args[i]; } } return result; } } } '@ } Function _InvokeScriptBlock($ScriptBlock, $Computer, $Credentials, $ArgumentList) { if (-not $Computer) { return Invoke-Command -ScriptBlock $scriptBlock -ArgumentList $ArgumentList } elseif ($Computer -is [System.Management.Automation.Runspaces.PSSession]) { return Invoke-Command -ScriptBlock $scriptBlock -Session $Computer -ArgumentList $ArgumentList } return Invoke-Command -ScriptBlock $scriptBlock -ComputerName $Computer -Credential $Credential -ArgumentList $ArgumentList } Function _IsWildcard($Item) { return $Item -match '\*|\[.+\]' } Function _Log { Param ( [Parameter(ValueFromPipeline=$true)] [object] $Message, [Parameter()] [string] $Caller, [Parameter()] [switch] $Force ) if(($VerbosePreference -ne 'Continue') -and (-not $Force.IsPresent)) { # No verbose set. Exit now to avoid expensive/unnecessary calls to Get-PSCallStack and Write-Verbose return } if(-not $Caller) { $caller = _GetLogCallStack } Write-Verbose "[$([DateTime]::Now.ToString('HH:mm:ss.ffff'))] [$caller] $Message" } Function _GetLogCallStack { $cs = [System.Collections.Stack]::new() foreach($frame in Get-PSCallStack) { if ($frame.Command -in @('_Log', '_GetLogCallStack', '', $null)) { continue } $cs.Push($frame.Command.Trim('_')) if ($frame.Command -like '*-*') { break } } return $cs.ToArray() -join ':' } Function _DumpObj { Param ( [Parameter(ValueFromPipeline=$true, Position=0)] [object] $InputObject, [Parameter()] $Depth = 5 ) return $InputObject | ConvertTo-Json -Depth $Depth -Compress } function _NewDictionary { #requires -Version 2.0 [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true, Position=1)] [ValidateNotNull()] [hashtable] $InputObject, [Parameter(Position=0)] [ValidateCount(2,2)] [Type[]] $Types = @([string], [object]) ) process { $KeyType = $Types[0] $ValueType = $Types[1] $outputObject = New-Object "System.Collections.Generic.Dictionary[[$($KeyType.FullName)],[$($ValueType.FullName)]]" foreach ($entry in $InputObject.GetEnumerator()) { $newKey = $entry.Key -as $KeyType if ($null -eq $newKey) { throw 'Could not convert key "{0}" of type "{1}" to type "{2}"' -f $entry.Key, $entry.Key.GetType().FullName, $KeyType.FullName } elseif ($outputObject.ContainsKey($newKey)) { throw "Duplicate key `"$newKey`" detected in input object." } $outputObject.Add($newKey, $entry.Value -as $ValueType) } Write-Output $outputObject } } Function _NewScriptBlock($EntryPoint, [string[]]$Dependency) { $entryPoint = (Get-Item "function:$EntryPoint").Definition.Trim() $paramSection = $entryPoint.Substring(0, $entryPoint.IndexOf("`n")) $bodySection = $entryPoint.Substring($paramSection.Length) + "`n`n" $body = $paramSection foreach($depFn in $Dependency) { $f = Get-Item "function:$depFn" $body += "Function $f `n{`n" $body += $f.Definition $body += "`n}`n`n" } $body += $bodySection return [scriptblock]::Create($body) } Function _NormalizeNodePath { Param ( [Parameter(Mandatory=$true, Position=0)] [AllowEmptyString()] [string] $Path, [Parameter(Mandatory=$true)] [string] $Project, [Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.TreeStructureGroup] $Scope, [switch] $IncludeScope, [switch] $ExcludePath, [switch] $IncludeLeadingBackslash, [switch] $IncludeTrailingBackslash, [switch] $IncludeTeamProject ) _Log "Normalizing path '$Path' with arguments $(_DumpObj $PSBoundParameters)" $newPath = '' $scopeName = $Scope.ToString().TrimEnd('s') if ($IncludeLeadingBackslash) { $newPath += '\' } if ($IncludeTeamProject) { $newPath += $Project + '\' } if ($IncludeScope) { $newPath += $scopeName + '\' } if(-not $ExcludePath.IsPresent) { $Path = $Path.Trim(' ', '\') if ($Path -like "$Project\$scopeName\*") { $Path = $Path.Substring("$Project\$scopeName\".Length) } if ($Path -like "$Project\*") { $Path = $Path.Substring($Path.IndexOf('\')) } elseif ($Path -eq $Project) { $Path = '' } $newPath += $Path } if ($newPath.EndsWith('\') -and (-not $IncludeTrailingBackslash.IsPresent)) { $newPath = $newPath.TrimEnd('\') } _Log "Normalized path: $newPath" return $newPath -replace '\\{2,}', '\' } Function _RegisterAssemblyResolver { if ($TfsCmdletsDebugStartup) { Write-Host "Entering TfsCmdlets startup debug mode" -ForegroundColor DarkYellow # For some reason, setting VerbosePreference here breaks the script. Using Set-Alias to work around it Set-Alias Write-Verbose Write-Host -Option Private } Write-Verbose 'Registering custom Assembly Resolver' if (-not ([System.Management.Automation.PSTypeName]'TfsCmdlets.AssemblyResolver').Type) { Write-Verbose "Compiling $PSEdition version of the assembly resolver" $sourcePath = (Join-Path $PSScriptRoot "../_cs/$($PSEdition)AssemblyResolver.cs" -Resolve) $sourceText = (Get-Content $sourcePath -Raw) Add-Type -TypeDefinition $sourceText -Language CSharp $libPath = (Join-Path $PSScriptRoot '../Lib' -Resolve) $assemblies = [System.Collections.Generic.Dictionary[string,string]]::new() Write-Verbose "Enumerating assemblies from $libPath" foreach($f in (Get-ChildItem $libPath -Filter '*.dll')) { Write-Verbose "Adding $f to list of private assemblies" $assemblies.Add($f.BaseName, $f.FullName) } Write-Verbose 'Calling AssemblyResolver.Register()' [TfsCmdlets.AssemblyResolver]::Register($assemblies, [bool]($TfsCmdletsDebugStartup)) } else { Write-Verbose 'Custom Assembly Resolver already registered; skipping' } } Function _TestGuid([string]$guid) { if([string]::IsNullOrEmpty($guid)) { return $false } $parsedGuid = [guid]::Empty return [guid]::TryParse($guid, [ref] $parsedGuid) } Function _TestRegistryValue { Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] $Value ) Process { try { _GetRegistryValue -Path $Path -Value $Value | Out-Null return $true } finally {} return $false } } Function _Throw { Param ( [Parameter(ValueFromPipeline=$true, Position=0)] [object] $Message, [Parameter(Position=1)] [object] $Exceptions ) $caller = (Get-PSCallStack)[1].Command if ($Exceptions) { $Message += "`n`nAdditional error information: $($Exceptions | ForEach-Object{ "$_"})" } throw "[$caller] $Message`n`n" } |