Improve Windows helpers functions stability (#723)

* Rework windows InstallHelpers; Add retry logic to install function
This commit is contained in:
Maksim Petrov
2020-04-21 11:58:27 +03:00
committed by GitHub
parent 54a14f61b3
commit 8e8fbb4f76
15 changed files with 197 additions and 227 deletions

View File

@@ -1,163 +1,151 @@
function Install-MSI
function Install-Binary
{
Param
(
[String]$MsiUrl,
[String]$MsiName,
[int]$retries = 5
<#
.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
)
$exitCode = -1
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 "Downloading $MsiName..."
$FilePath = "${env:Temp}\$MsiName"
Write-Host "Starting Install $Name..."
$process = Start-Process -FilePath $filePath -ArgumentList $ArgumentList -Wait -PassThru
Invoke-WebRequest -Uri $MsiUrl -OutFile $FilePath
$Arguments = ('/i', $FilePath, '/QN', '/norestart' )
Write-Host "Starting Install $MsiName..."
$process = Start-Process -FilePath msiexec.exe -ArgumentList $Arguments -Wait -PassThru
$exitCode = $process.ExitCode
if ($exitCode -eq 0 -or $exitCode -eq 3010)
{
Write-Host -Object 'Installation successful'
return $exitCode
Write-Host "Installation successful"
}
else
{
Write-Host -Object "Non zero exit code returned by the installation process : $exitCode."
Write-Host "Non zero exit code returned by the installation process: $exitCode"
exit $exitCode
}
}
catch
{
Write-Host -Object "Failed to install the MSI $MsiName"
Write-Host -Object $_.Exception.Message
exit -1
}
}
function Install-EXE
{
Param
(
[String]$Url,
[String]$Name,
[String[]]$ArgumentList
)
$exitCode = -1
try
{
Write-Host "Downloading $Name..."
$FilePath = "${env:Temp}\$Name"
Invoke-WebRequest -Uri $Url -OutFile $FilePath
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 -Object 'Installation successful'
return $exitCode
}
else
{
Write-Host -Object "Non zero exit code returned by the installation process : $exitCode."
return $exitCode
}
}
catch
{
Write-Host -Object "Failed to install the Executable $Name"
Write-Host -Object $_.Exception.Message
return -1
Write-Host "Failed to install the $fileExtension ${Name}: $($_.Exception.Message)"
exit 1
}
}
function Stop-SvcWithErrHandling
<#
.DESCRIPTION
Function for stopping the Windows Service with error handling
.AUTHOR
Andrey Mishechkin v-andmis@microsoft.com
.PARAMETER -ServiceName
The name of stopping service
.PARAMETER -StopOnError
Switch for stopping the script and exit from PowerShell if one service is absent
#>
{
<#
.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,
[Parameter()] [switch] $StopOnError
[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;
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";
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 {
catch
{
Write-Error "[!] Failed to stop service [$ServiceName] with error:"
$_ | Out-String | Write-Error;
$_ | Out-String | Write-Error
}
}
}
}
function Set-SvcWithErrHandling
<#
.DESCRIPTION
Function for setting the Windows Service parameter with error handling
.AUTHOR
Andrey Mishechkin v-andmis@microsoft.com
.PARAMETER -ServiceName
The name of stopping service
.PARAMETER -Arguments
Hashtable for service arguments
#>
{
<#
.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
[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";
Process
{
$service = Get-Service $ServiceName -ErrorAction SilentlyContinue
if (-not $service)
{
Write-Warning "[!] Service [$ServiceName] is not found"
}
try
{
Set-Service $serviceName @Arguments
}
try {
Set-Service $ServiceName @Arguments;
}
catch {
catch
{
Write-Error "[!] Failed to set service [$ServiceName] arguments with error:"
$_ | Out-String | Write-Error;
$_ | Out-String | Write-Error
}
}
}
@@ -173,36 +161,36 @@ function Start-DownloadWithRetry
[int] $Retries = 20
)
$FilePath = Join-Path -Path $DownloadPath -ChildPath $Name
$filePath = Join-Path -Path $DownloadPath -ChildPath $Name
#Default retry logic for the package.
while ($retries -gt 0)
while ($Retries -gt 0)
{
try
{
Write-Host "Downloading package from: $Url to path $FilePath ."
(New-Object System.Net.WebClient).DownloadFile($Url, $FilePath)
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--
$Retries--
if ($retries -eq 0)
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"
Write-Host "Waiting 30 seconds before retrying. Retries left: $Retries"
Start-Sleep -Seconds 30
}
}
return $FilePath
return $filePath
}
function Install-VsixExtension
{
Param
@@ -217,12 +205,12 @@ function Install-VsixExtension
[switch] $InstallOnly
)
if (!$InstallOnly)
{
$FilePath = Start-DownloadWithRetry -Url $Url -Name $Name
}
if (-not $InstallOnly)
{
$FilePath = Start-DownloadWithRetry -Url $Url -Name $Name
}
$ArgumentList = ('/quiet', "`"$FilePath`"")
$argumentList = ('/quiet', "`"$FilePath`"")
Write-Host "Starting Install $Name..."
try
@@ -230,7 +218,7 @@ function Install-VsixExtension
#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
$process = Start-Process -FilePath "C:\Program Files (x86)\Microsoft Visual Studio\$VSversion\Enterprise\Common7\IDE\VSIXInstaller.exe" -ArgumentList $argumentList -Wait -PassThru
}
else
{
@@ -257,20 +245,20 @@ function Install-VsixExtension
}
#Cleanup downloaded installation files
if (!$InstallOnly)
{
Remove-Item -Force -Confirm:$false $FilePath
}
if (-not $InstallOnly)
{
Remove-Item -Force -Confirm:$false $FilePath
}
}
function Get-VSExtensionVersion
{
param (
[string] [Parameter(Mandatory=$true)] $packageName
[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"
@@ -281,7 +269,7 @@ function Get-VSExtensionVersion
$state = $stateContent | ConvertFrom-Json
$packageVersion = ($state.packages | Where-Object { $_.id -eq $packageName }).version
if (!$packageVersion)
if (-not $packageVersion)
{
Write-Host "installed package $packageName for Visual Studio 2019 was not found"
exit 1
@@ -290,7 +278,6 @@ function Get-VSExtensionVersion
return $packageVersion
}
function Get-ToolcachePackages {
$toolcachePath = Join-Path $env:ROOT_FOLDER "toolcache.json"
Get-Content -Raw $toolcachePath | ConvertFrom-Json