Files
runner-images/images/win/scripts/ImageHelpers/InstallHelpers.ps1
Maksim Petrov 8d0d6f85cc [Windows] Merge several toolset provisioners into single Configure-Toolset script (#1111)
* Add Configure-Toolset for windows

* Set error action preference to stop

* Move toolcache.json to config folder

* Fix in verbose

* Rename toolsets folder

* Move default version functions from install-toolset to configure-toolset

* Rework Configure-Toolset

* Fix typo and remove empty line

* Fix issues

* Fix parameters in helpers

* Fix helper syntax

* Rename defaultVariable
2020-06-30 07:48:55 +03:00

468 lines
12 KiB
PowerShell

function Install-Binary
{
<#
.SYNOPSIS
A helper function to install executables.
.DESCRIPTION
Download and install .exe or .msi binaries from specified URL.
.PARAMETER Url
The URL from which the binary will be downloaded. Required parameter.
.PARAMETER Name
The Name with which binary will be downloaded. Required parameter.
.PARAMETER ArgumentList
The list of arguments that will be passed to the installer. Required for .exe binaries.
.EXAMPLE
Install-Binary -Url "https://go.microsoft.com/fwlink/p/?linkid=2083338" -Name "winsdksetup.exe" -ArgumentList ("/features", "+", "/quiet")
#>
Param
(
[Parameter(Mandatory)]
[String] $Url,
[Parameter(Mandatory)]
[String] $Name,
[String[]] $ArgumentList
)
Write-Host "Downloading $Name..."
$filePath = Start-DownloadWithRetry -Url $Url -Name $Name
# MSI binaries should be installed via msiexec.exe
$fileExtension = ([System.IO.Path]::GetExtension($Name)).Replace(".", "")
if ($fileExtension -eq "msi")
{
$ArgumentList = ('/i', $filePath, '/QN', '/norestart')
$filePath = "msiexec.exe"
}
try
{
Write-Host "Starting Install $Name..."
$process = Start-Process -FilePath $filePath -ArgumentList $ArgumentList -Wait -PassThru
$exitCode = $process.ExitCode
if ($exitCode -eq 0 -or $exitCode -eq 3010)
{
Write-Host "Installation successful"
}
else
{
Write-Host "Non zero exit code returned by the installation process: $exitCode"
exit $exitCode
}
}
catch
{
Write-Host "Failed to install the $fileExtension ${Name}: $($_.Exception.Message)"
exit 1
}
}
Function Install-VisualStudio
{
<#
.SYNOPSIS
A helper function to install Visual Studio.
.DESCRIPTION
Prepare system environment, and install Visual Studio bootstrapper with selected workloads.
.PARAMETER BootstrapperUrl
The URL from which the bootstrapper will be downloaded. Required parameter.
.PARAMETER WorkLoads
The string that contain workloads that will be passed to the installer.
#>
Param
(
[Parameter(Mandatory)]
[String] $BootstrapperUrl,
[String] $WorkLoads
)
Write-Host "Downloading Bootstrapper ..."
$BootstrapperName = [IO.Path]::GetFileName($BootstrapperUrl)
$bootstrapperFilePath = Start-DownloadWithRetry -Url $BootstrapperUrl -Name $BootstrapperName
try
{
Write-Host "Enable short name support on Windows needed for Xamarin Android AOT, defaults appear to have been changed in Azure VMs"
$shortNameEnableProcess = Start-Process -FilePath fsutil.exe -ArgumentList ('8dot3name', 'set', '0') -Wait -PassThru
$shortNameEnableExitCode = $shortNameEnableProcess.ExitCode
if ($shortNameEnableExitCode -ne 0)
{
Write-Host "Enabling short name support on Windows failed. This needs to be enabled prior to VS 2017 install for Xamarin Andriod AOT to work."
exit $shortNameEnableExitCode
}
Write-Host "Starting Install ..."
$bootstrapperArgumentList = ('/c', $bootstrapperFilePath, $WorkLoads, '--quiet', '--norestart', '--wait', '--nocache' )
$process = Start-Process -FilePath cmd.exe -ArgumentList $bootstrapperArgumentList -Wait -PassThru
$exitCode = $process.ExitCode
if ($exitCode -eq 0 -or $exitCode -eq 3010)
{
Write-Host "Installation successful"
return $exitCode
}
else
{
Write-Host "Non zero exit code returned by the installation process : $exitCode"
exit $exitCode
}
}
catch
{
Write-Host "Failed to install Visual Studio; $($_.Exception.Message)"
exit -1
}
}
function Stop-SvcWithErrHandling
{
<#
.DESCRIPTION
Function for stopping the Windows Service with error handling
.PARAMETER ServiceName
The name of stopping service
.PARAMETER StopOnError
Switch for stopping the script and exit from PowerShell if one service is absent
#>
Param
(
[Parameter(Mandatory, ValueFromPipeLine = $true)]
[string] $ServiceName,
[switch] $StopOnError
)
Process
{
$service = Get-Service $ServiceName -ErrorAction SilentlyContinue
if (-not $service)
{
Write-Warning "[!] Service [$ServiceName] is not found"
if ($StopOnError)
{
exit 1
}
}
else
{
Write-Host "Try to stop service [$ServiceName]"
try
{
Stop-Service -Name $ServiceName -Force
$service.WaitForStatus("Stopped", "00:01:00")
Write-Host "Service [$ServiceName] has been stopped successfuly"
}
catch
{
Write-Error "[!] Failed to stop service [$ServiceName] with error:"
$_ | Out-String | Write-Error
}
}
}
}
function Set-SvcWithErrHandling
{
<#
.DESCRIPTION
Function for setting the Windows Service parameter with error handling
.PARAMETER ServiceName
The name of stopping service
.PARAMETER Arguments
Hashtable for service arguments
#>
Param
(
[Parameter(Mandatory, ValueFromPipeLine = $true)]
[string] $ServiceName,
[Parameter(Mandatory)]
[hashtable] $Arguments
)
Process
{
$service = Get-Service $ServiceName -ErrorAction SilentlyContinue
if (-not $service)
{
Write-Warning "[!] Service [$ServiceName] is not found"
}
try
{
Set-Service $serviceName @Arguments
}
catch
{
Write-Error "[!] Failed to set service [$ServiceName] arguments with error:"
$_ | Out-String | Write-Error
}
}
}
function Start-DownloadWithRetry
{
Param
(
[Parameter(Mandatory)]
[string] $Url,
[string] $Name,
[string] $DownloadPath = "${env:Temp}",
[int] $Retries = 20
)
if ([String]::IsNullOrEmpty($Name)) {
$Name = [IO.Path]::GetFileName($Url)
}
$filePath = Join-Path -Path $DownloadPath -ChildPath $Name
#Default retry logic for the package.
while ($Retries -gt 0)
{
try
{
Write-Host "Downloading package from: $Url to path $filePath ."
(New-Object System.Net.WebClient).DownloadFile($Url, $filePath)
break
}
catch
{
Write-Host "There is an error during package downloading:`n $_"
$Retries--
if ($Retries -eq 0)
{
Write-Host "File can't be downloaded. Please try later or check that file exists by url: $Url"
exit 1
}
Write-Host "Waiting 30 seconds before retrying. Retries left: $Retries"
Start-Sleep -Seconds 30
}
}
return $filePath
}
function Install-VsixExtension
{
Param
(
[string] $Url,
[Parameter(Mandatory = $true)]
[string] $Name,
[string] $FilePath,
[Parameter(Mandatory = $true)]
[string] $VSversion,
[int] $retries = 20,
[switch] $InstallOnly
)
if (-not $InstallOnly)
{
$FilePath = Start-DownloadWithRetry -Url $Url -Name $Name
}
$argumentList = ('/quiet', "`"$FilePath`"")
Write-Host "Starting Install $Name..."
try
{
#There are 2 types of packages at the moment - exe and vsix
if ($Name -match "vsix")
{
$process = Start-Process -FilePath "C:\Program Files (x86)\Microsoft Visual Studio\$VSversion\Enterprise\Common7\IDE\VSIXInstaller.exe" -ArgumentList $argumentList -Wait -PassThru
}
else
{
$process = Start-Process -FilePath ${env:Temp}\$Name /Q -Wait -PassThru
}
}
catch
{
Write-Host "There is an error during $Name installation"
$_
exit 1
}
$exitCode = $process.ExitCode
if ($exitCode -eq 0 -or $exitCode -eq 1001) # 1001 means the extension is already installed
{
Write-Host "$Name installed successfully"
}
else
{
Write-Host "Unsuccessful exit code returned by the installation process: $exitCode."
exit 1
}
#Cleanup downloaded installation files
if (-not $InstallOnly)
{
Remove-Item -Force -Confirm:$false $FilePath
}
}
function Get-VSExtensionVersion
{
Param
(
[Parameter(Mandatory=$true)]
[string] $packageName
)
$instanceFolders = Get-ChildItem -Path "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances"
if ($instanceFolders -is [array])
{
Write-Host "More than one instance installed"
exit 1
}
$stateContent = Get-Content -Path (Join-Path $instanceFolders.FullName '\state.packages.json')
$state = $stateContent | ConvertFrom-Json
$packageVersion = ($state.packages | Where-Object { $_.id -eq $packageName }).version
if (-not $packageVersion)
{
Write-Host "installed package $packageName for Visual Studio 2019 was not found"
exit 1
}
return $packageVersion
}
function Get-ToolcachePackages
{
$toolcachePath = Join-Path $env:ROOT_FOLDER "toolcache.json"
Get-Content -Raw $toolcachePath | ConvertFrom-Json
}
function Get-ToolsetContent
{
$toolsetJson = Get-Content -Path $env:TOOLSET_JSON_PATH -Raw
ConvertFrom-Json -InputObject $toolsetJson
}
function Get-ToolsetToolFullPath
{
<#
.DESCRIPTION
Function that return full path to specified toolset tool.
.PARAMETER Name
The name of required tool.
.PARAMETER Version
The version of required tool.
.PARAMETER Arch
The architecture of required tool.
#>
Param
(
[Parameter(Mandatory=$true)]
[string] $Name,
[Parameter(Mandatory=$true)]
[string] $Version,
[string] $Arch = "x64"
)
$ToolPath = Join-Path $env:AGENT_TOOLSDIRECTORY $Name
# Add wildcard if missing
if ($Version.Split(".").Length -lt 3) {
$Version += ".*"
}
# Check if version folder exists
$expectedVersionPath = Join-Path $ToolPath $Version
if (-not (Test-Path $expectedVersionPath)) {
Write-Host "Expected ${Name} ${Version} folder is not found!"
exit 1
}
# Take latest installed version in case if toolset version contains wildcards
$foundVersion = Get-Item $expectedVersionPath `
| Sort-Object -Property {[version]$_.name} -Descending `
| Select-Object -First 1
# Check for required architecture folder
$foundVersionArchPath = Join-Path $foundVersion $Arch
if (-not (Test-Path $foundVersionArchPath)) {
Write-Host "Expected ${Name}(${Arch}) $($foundVersion.name) architecture folder is not found!"
exit 1
}
return $foundVersionArchPath
}
function Get-ToolsByName
{
Param
(
[Parameter(Mandatory = $True)]
[string]$SoftwareName
)
(Get-ToolcachePackages).PSObject.Properties | Where-Object { $_.Name -match $SoftwareName } | ForEach-Object {
$packageNameParts = $_.Name.Split("-")
[PSCustomObject] @{
ToolName = $packageNameParts[1]
Versions = $_.Value
Architecture = $packageNameParts[3,4] -join "-"
}
}
}
function Get-WinVersion
{
(Get-CimInstance -ClassName Win32_OperatingSystem).Caption
}
function Test-IsWin19
{
(Get-WinVersion) -match "2019"
}
function Test-IsWin16
{
(Get-WinVersion) -match "2016"
}
function Extract-7Zip {
Param
(
[Parameter(Mandatory=$true)]
[string]$Path,
[Parameter(Mandatory=$true)]
[string]$DestinationPath
)
Write-Host "Expand archive '$PATH' to '$DestinationPath' directory"
7z.exe x "$Path" -o"$DestinationPath" -y | Out-Null
if ($LASTEXITCODE -ne 0)
{
Write-Host "There is an error during expanding '$Path' to '$DestinationPath' directory"
exit 1
}
}