Functions/Install-WhiskeyTool.ps1
function Install-WhiskeyTool { <# .SYNOPSIS Downloads and installs tools needed by the Whiskey module. .DESCRIPTION The `Install-WhiskeyTool` function downloads and installs PowerShell modules or NuGet Packages needed by functions in the Whiskey module. PowerShell modules are installed to a `Modules` directory in your build root. A `DirectoryInfo` object for the downloaded tool's directory is returned. `Install-WhiskeyTool` also installs tools that are needed by tasks. Tasks define the tools they need with a [Whiskey.RequiresTool()] attribute in the tasks function. Supported tools are 'Node', 'NodeModule', and 'DotNet'. Users of the `Whiskey` API typcially won't need to use this function. It is called by other `Whiskey` function so they ahve the tools they need. .EXAMPLE Install-WhiskeyTool -NugetPackageName 'NUnit.Runners' -version '2.6.4' Demonstrates how to install a specific version of a NuGet Package. In this case, NUnit Runners version 2.6.4 would be installed. #> [CmdletBinding()] param( [Parameter(Mandatory=$true,ParameterSetName='Tool')] [Whiskey.RequiresToolAttribute] # The attribute that defines what tool is necessary. $ToolInfo, [Parameter(Mandatory=$true,ParameterSetName='Tool')] [string] # The directory where you want the tools installed. $InstallRoot, [Parameter(Mandatory=$true,ParameterSetName='Tool')] [hashtable] # The task parameters for the currently running task. $TaskParameter, [Parameter(ParameterSetName='Tool')] [Switch] # Running in clean mode, so don't install the tool if it isn't installed. $InCleanMode, [Parameter(Mandatory=$true,ParameterSetName='NuGet')] [string] # The name of the NuGet package to download. $NuGetPackageName, [Parameter(ParameterSetName='NuGet')] [string] # The version of the package to download. Must be a three part number, i.e. it must have a MAJOR, MINOR, and BUILD number. $Version, [Parameter(Mandatory=$true,ParameterSetName='NuGet')] [string] # The root directory where the tools should be downloaded. The default is your build root. # # PowerShell modules are saved to `$DownloadRoot\Modules`. # # NuGet packages are saved to `$DownloadRoot\packages`. $DownloadRoot ) Set-StrictMode -Version 'Latest' Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState $mutexName = $InstallRoot if( $DownloadRoot ) { $mutexName = $DownloadRoot } # Back slashes in mutex names are reserved. $mutexName = $mutexName -replace '\\','/' $mutexName = $mutexName -replace '/','-' $startedWaitingAt = Get-Date $startedUsingAt = Get-Date Write-Debug -Message ('Creating mutex "{0}".' -f $mutexName) $installLock = New-Object 'Threading.Mutex' $false,$mutexName #$DebugPreference = 'Continue' Write-Debug -Message ('[{0:yyyy-MM-dd HH:mm:ss}] Process "{1}" is waiting for mutex "{2}".' -f (Get-Date),$PID,$mutexName) try { try { [void]$installLock.WaitOne() } catch [Threading.AbandonedMutexException] { Write-Debug -Message ('[{0:yyyy-MM-dd HH:mm:ss}] Process "{1}" caught "{2}" exception waiting to acquire mutex "{3}": {4}.' -f (Get-Date),$PID,$_.Exception.GetType().FullName,$mutexName,$_) $Global:Error.RemoveAt(0) } $waitedFor = (Get-Date) - $startedWaitingAt Write-Debug -Message ('[{0:yyyy-MM-dd HH:mm:ss}] Process "{1}" obtained mutex "{2}" in {3}.' -f (Get-Date),$PID,$mutexName,$waitedFor) #$DebugPreference = 'SilentlyContinue' $startedUsingAt = Get-Date if( $PSCmdlet.ParameterSetName -eq 'NuGet' ) { if( -not $IsWindows ) { Write-Error -Message ('Unable to install NuGet-based package {0} {1}: NuGet.exe is only supported on Windows.' -f $NuGetPackageName,$Version) -ErrorAction Stop return } $nugetPath = Join-Path -Path $PSScriptRoot -ChildPath '..\bin\NuGet.exe' -Resolve $packagesRoot = Join-Path -Path $DownloadRoot -ChildPath 'packages' $version = Resolve-WhiskeyNuGetPackageVersion -NuGetPackageName $NuGetPackageName -Version $Version -NugetPath $nugetPath if( -not $Version ) { return } $nuGetRootName = '{0}.{1}' -f $NuGetPackageName,$Version $nuGetRoot = Join-Path -Path $packagesRoot -ChildPath $nuGetRootName Set-Item -Path 'env:EnableNuGetPackageRestore' -Value 'true' if( -not (Test-Path -Path $nuGetRoot -PathType Container) ) { & $nugetPath install $NuGetPackageName -version $Version -outputdirectory $packagesRoot | Write-CommandOutput -Description ('nuget.exe install') } return $nuGetRoot } elseif( $PSCmdlet.ParameterSetName -eq 'Tool' ) { $provider,$name = $ToolInfo.Name -split '::' if( -not $name ) { $name = $provider $provider = '' } $version = $TaskParameter[$ToolInfo.VersionParameterName] if( -not $version ) { $version = $ToolInfo.Version } switch( $provider ) { 'NodeModule' { $nodePath = Resolve-WhiskeyNodePath -BuildRootPath $InstallRoot if( -not $nodePath ) { Write-Error -Message ('It looks like Node isn''t installed in your repository. Whiskey usually installs Node for you into a .node directory. If this directory doesn''t exist, this is most likely a task authoring error and the author of your task needs to add a `WhiskeyTool` attribute declaring it has a dependency on Node. If the .node directory exists, the Node package is most likely corrupt. Please delete it and re-run your build.') -ErrorAction stop return } $moduleRoot = Install-WhiskeyNodeModule -Name $name ` -BuildRootPath $InstallRoot ` -Version $version ` -Global ` -InCleanMode:$InCleanMode ` -ErrorAction Stop $TaskParameter[$ToolInfo.PathParameterName] = $moduleRoot } 'PowerShellModule' { $moduleRoot = Install-WhiskeyPowerShellModule -Name $name -Version $version -ErrorAction Stop $TaskParameter[$ToolInfo.PathParameterName] = $moduleRoot } default { switch( $name ) { 'Node' { $TaskParameter[$ToolInfo.PathParameterName] = Install-WhiskeyNode -InstallRoot $InstallRoot -Version $version -InCleanMode:$InCleanMode } 'DotNet' { $TaskParameter[$ToolInfo.PathParameterName] = Install-WhiskeyDotNetTool -InstallRoot $InstallRoot -WorkingDirectory (Get-Location).ProviderPath -Version $version -ErrorAction Stop } default { throw ('Unknown tool ''{0}''. The only supported tools are ''Node'' and ''DotNet''.' -f $name) } } } } } } finally { #$DebugPreference = 'Continue' $usedFor = (Get-Date) - $startedUsingAt Write-Debug -Message ('[{0:yyyy-MM-dd HH:mm:ss}] Process "{1}" releasing mutex "{2}" after using it for {3}.' -f (Get-Date),$PID,$mutexName,$usedFor) $startedReleasingAt = Get-Date $installLock.ReleaseMutex(); $installLock.Dispose() $installLock.Close() $installLock = $null $releasedDuration = (Get-Date) - $startedReleasingAt Write-Debug -Message ('[{0:yyyy-MM-dd HH:mm:ss}] Process "{1}" released mutex "{2}" in {3}.' -f (Get-Date),$PID,$mutexName,$releasedDuration) #$DebugPreference = 'SilentlyContinue' } } |