2023-11-22 15:14:08 +01:00
function Install-Binary {
2020-04-21 11:58:27 +03:00
<#
. SYNOPSIS
2023-11-22 15:14:08 +01:00
A function to install binaries from either a URL or a local path.
2020-04-21 11:58:27 +03:00
. DESCRIPTION
2023-11-22 15:14:08 +01:00
This function downloads and installs .exe or .msi binaries from a specified URL or a local path. It also supports checking the binary's signature and SHA256/SHA512 sum before installation.
2020-04-21 11:58:27 +03:00
.PARAMETER Url
2023-11-22 15:14:08 +01:00
The URL from which the binary will be downloaded. This parameter is required if LocalPath is not specified.
2020-04-21 11:58:27 +03:00
2023-11-22 15:14:08 +01:00
.PARAMETER LocalPath
The local path of the binary to be installed. This parameter is required if Url is not specified.
2020-04-21 11:58:27 +03:00
2023-11-22 15:14:08 +01:00
.PARAMETER Type
The type of the binary to be installed. Valid values are "MSI" and "EXE". If not specified, the type is inferred from the file extension.
.PARAMETER InstallArgs
The list of arguments that will be passed to the installer. Cannot be used together with ExtraInstallArgs.
.PARAMETER ExtraInstallArgs
Additional arguments that will be passed to the installer. Cannot be used together with InstallArgs.
.PARAMETER ExpectedSignature
The expected signature of the binary. If specified, the binary's signature is checked before installation.
.PARAMETER ExpectedSHA256Sum
The expected SHA256 sum of the binary. If specified, the binary's SHA256 sum is checked before installation.
.PARAMETER ExpectedSHA512Sum
The expected SHA512 sum of the binary. If specified, the binary's SHA512 sum is checked before installation.
2020-04-21 11:58:27 +03:00
. EXAMPLE
2023-11-22 15:14:08 +01:00
Install-Binary -Url "https://go.microsoft.com/fwlink/p/?linkid=2083338" -Type EXE -InstallArgs ("/features", "+", "/quiet") -ExpectedSignature "A5C7D5B7C838D5F89DDBEDB85B2C566B4CDA881F"
2020-04-21 11:58:27 +03:00
#>
2020-05-05 20:50:00 +03:00
Param
(
2023-11-22 15:14:08 +01:00
[ Parameter ( Mandatory , ParameterSetName = "Url" )]
2020-04-21 11:58:27 +03:00
[ String ] $Url ,
2023-11-22 15:14:08 +01:00
[ Parameter ( Mandatory , ParameterSetName = "LocalPath" )]
[ String ] $LocalPath ,
[ ValidateSet ( "MSI" , "EXE" )]
[ String ] $Type ,
[ String[] ] $InstallArgs ,
[ String[] ] $ExtraInstallArgs ,
[ String[] ] $ExpectedSignature ,
[ String ] $ExpectedSHA256Sum ,
[ String ] $ExpectedSHA512Sum
2019-12-13 09:48:00 -05:00
)
2023-11-22 15:14:08 +01:00
if ( $PSCmdlet . ParameterSetName -eq "LocalPath" ) {
if ( -not ( Test-Path -Path $LocalPath )) {
throw "LocalPath parameter is specified, but the file does not exist."
}
if ( -not $Type ) {
$Type = ([ System.IO.Path ]:: GetExtension ( $LocalPath )). Replace ( "." , "" ). ToUpper ()
if ( $Type -ne "MSI" -and $Type -ne "EXE" ) {
throw "LocalPath parameter is specified, but the file extension is not .msi or .exe. Please specify the Type parameter."
}
}
$filePath = $LocalPath
} else {
if ( -not $Type ) {
$Type = ([ System.IO.Path ]:: GetExtension ( $Url )). Replace ( "." , "" ). ToUpper ()
if ( $Type -ne "MSI" -and $Type -ne "EXE" ) {
throw "Cannot determine the file type from the URL. Please specify the Type parameter."
}
}
$fileName = [ System.IO.Path ]:: GetFileNameWithoutExtension ([ System.IO.Path ]:: GetRandomFileName ()) + ". $Type " . ToLower ()
$filePath = Start-DownloadWithRetry -Url $Url -Name $fileName
2021-09-09 10:46:21 +03:00
}
2020-04-21 11:58:27 +03:00
2023-11-22 15:14:08 +01:00
if ( $PSBoundParameters . ContainsKey ( 'ExpectedSignature' )) {
if ( $ExpectedSignature ) {
2023-09-22 10:52:16 +02:00
Test-FileSignature -FilePath $filePath -ExpectedThumbprint $ExpectedSignature
2023-11-22 15:14:08 +01:00
} else {
2023-09-22 10:52:16 +02:00
throw "ExpectedSignature parameter is specified, but no signature is provided."
}
}
2023-11-22 15:14:08 +01:00
if ( $ExpectedSHA256Sum ) {
$fileHash = ( Get-FileHash -Path $filePath -Algorithm SHA256 ). Hash
Use-ChecksumComparison $fileHash $ExpectedSHA256Sum
2020-04-21 11:58:27 +03:00
}
2019-12-13 09:48:00 -05:00
2023-11-22 15:14:08 +01:00
if ( $ExpectedSHA512Sum ) {
$fileHash = ( Get-FileHash -Path $filePath -Algorithm SHA512 ). Hash
Use-ChecksumComparison $fileHash $ExpectedSHA512Sum
}
if ( $ExtraInstallArgs -and $InstallArgs ) {
throw "InstallArgs and ExtraInstallArgs parameters cannot be used together."
}
if ( $Type -eq "MSI" ) {
# MSI binaries should be installed via msiexec.exe
if ( $ExtraInstallArgs ) {
$InstallArgs = @ ( '/i' , $filePath , '/qn' , '/norestart' ) + $ExtraInstallArgs
} elseif ( -not $InstallArgs ) {
Write-Host "No arguments provided for MSI binary. Using default arguments: /i, /qn, /norestart"
$InstallArgs = @ ( '/i' , $filePath , '/qn' , '/norestart' )
2019-12-13 09:48:00 -05:00
}
2023-11-22 15:14:08 +01:00
$filePath = "msiexec.exe"
} else {
# EXE binaries should be started directly
if ( $ExtraInstallArgs ) {
$InstallArgs = $ExtraInstallArgs
2019-12-13 09:48:00 -05:00
}
}
2023-11-22 15:14:08 +01:00
$installStartTime = Get-Date
Write-Host "Starting Install $Name ..."
try {
$process = Start-Process -FilePath $filePath -ArgumentList $InstallArgs -Wait -PassThru
$exitCode = $process . ExitCode
2021-09-20 16:20:56 +03:00
$installCompleteTime = [ math ]:: Round (( $ ( Get-Date ) - $installStartTime ). TotalSeconds , 2 )
2023-11-22 15:14:08 +01:00
if ( $exitCode -eq 0 ) {
Write-Host "Installation successful in $installCompleteTime seconds"
} elseif ( $exitCode -eq 3010 ) {
Write-Host "Installation successful in $installCompleteTime seconds. Reboot is required."
} else {
Write-Host "Installation process returned unexpected exit code: $exitCode "
Write-Host "Time elapsed: $installCompleteTime seconds"
exit $exitCode
}
} catch {
$installCompleteTime = [ math ]:: Round (( $ ( Get-Date ) - $installStartTime ). TotalSeconds , 2 )
Write-Host "Installation failed in $installCompleteTime seconds"
2019-12-13 09:48:00 -05:00
}
}
2019-12-23 17:41:24 +04:00
2023-11-10 16:25:40 +01:00
function Stop-SvcWithErrHandling {
2020-04-21 11:58:27 +03:00
<#
. 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
#>
2023-11-10 16:25:40 +01:00
Param (
2020-04-21 11:58:27 +03:00
[ Parameter ( Mandatory , ValueFromPipeLine = $true )]
[ string ] $ServiceName ,
[ switch ] $StopOnError
2019-12-23 17:41:24 +04:00
)
2023-11-10 16:25:40 +01:00
Process {
2020-04-21 11:58:27 +03:00
$service = Get-Service $ServiceName -ErrorAction SilentlyContinue
2023-11-10 16:25:40 +01:00
if ( -not $service ) {
2020-04-21 11:58:27 +03:00
Write-Warning "[!] Service [ $ServiceName ] is not found"
2023-11-10 16:25:40 +01:00
if ( $StopOnError ) {
2020-04-21 11:58:27 +03:00
exit 1
2019-12-23 17:41:24 +04:00
}
2023-11-10 16:25:40 +01:00
} else {
2020-04-21 11:58:27 +03:00
Write-Host "Try to stop service [ $ServiceName ]"
2023-11-10 16:25:40 +01:00
try {
2020-04-21 11:58:27 +03:00
Stop-Service -Name $ServiceName -Force
$service . WaitForStatus ( "Stopped" , "00:01:00" )
Write-Host "Service [ $ServiceName ] has been stopped successfuly"
2023-11-10 16:25:40 +01:00
} catch {
2019-12-23 17:41:24 +04:00
Write-Error "[!] Failed to stop service [ $ServiceName ] with error:"
2020-04-21 11:58:27 +03:00
$_ | Out-String | Write-Error
2019-12-23 17:41:24 +04:00
}
}
}
}
2023-11-10 16:25:40 +01:00
function Set-SvcWithErrHandling {
2020-04-21 11:58:27 +03:00
<#
. DESCRIPTION
Function for setting the Windows Service parameter with error handling
.PARAMETER ServiceName
The name of stopping service
.PARAMETER Arguments
Hashtable for service arguments
2023-11-10 16:25:40 +01:00
.PARAMETER StopOnError
Switch for stopping the script and exit from PowerShell if one service is absent
2020-04-21 11:58:27 +03:00
#>
2020-02-19 19:59:48 +03:00
2023-11-10 16:25:40 +01:00
Param (
2020-04-21 11:58:27 +03:00
[ Parameter ( Mandatory , ValueFromPipeLine = $true )]
[ string ] $ServiceName ,
[ Parameter ( Mandatory )]
2023-11-10 16:25:40 +01:00
[ hashtable ] $Arguments ,
[ switch ] $StopOnError
2019-12-23 17:41:24 +04:00
)
2023-11-10 16:25:40 +01:00
Process {
2020-04-21 11:58:27 +03:00
$service = Get-Service $ServiceName -ErrorAction SilentlyContinue
2023-11-10 16:25:40 +01:00
if ( -not $service ) {
Write-Warning "[!] Service [ $ServiceName ] is not found"
if ( $StopOnError ) {
exit 1
}
} else {
try {
Set-Service $serviceName @Arguments
} catch {
Write-Error "[!] Failed to set service [ $ServiceName ] arguments with error:"
$_ | Out-String | Write-Error
2020-04-21 11:58:27 +03:00
}
2019-12-23 17:41:24 +04:00
}
}
}
2020-02-19 19:59:48 +03:00
2023-11-22 15:14:08 +01:00
function Start-DownloadWithRetry {
2020-06-30 07:48:55 +03:00
Param
2020-05-05 20:50:00 +03:00
(
2020-03-19 18:15:11 +07:00
[ Parameter ( Mandatory )]
[ string ] $Url ,
[ string ] $Name ,
2020-03-24 17:35:11 +07:00
[ string ] $DownloadPath = " ${env:Temp} " ,
2020-04-02 23:29:22 +03:00
[ int ] $Retries = 20
)
2020-03-19 02:38:23 +07:00
2020-05-15 15:01:45 +03:00
if ([ String ]:: IsNullOrEmpty ( $Name )) {
$Name = [ IO.Path ]:: GetFileName ( $Url )
}
2020-04-21 11:58:27 +03:00
$filePath = Join-Path -Path $DownloadPath -ChildPath $Name
2021-09-20 16:20:56 +03:00
$downloadStartTime = Get-Date
2022-04-20 16:23:32 +04:00
2021-09-20 16:20:56 +03:00
# Default retry logic for the package.
2023-11-22 15:14:08 +01:00
Write-Host "Downloading package from: $Url to path $filePath ."
while ( $Retries -gt 0 ) {
try {
2021-09-20 16:20:56 +03:00
$downloadAttemptStartTime = Get-Date
2020-04-21 11:58:27 +03:00
( New-Object System . Net . WebClient ). DownloadFile ( $Url , $filePath )
2020-04-02 23:29:22 +03:00
break
2023-11-22 15:14:08 +01:00
} catch {
2021-09-20 16:20:56 +03:00
$failTime = [ math ]:: Round (( $ ( Get-Date ) - $downloadStartTime ). TotalSeconds , 2 )
$attemptTime = [ math ]:: Round (( $ ( Get-Date ) - $downloadAttemptStartTime ). TotalSeconds , 2 )
2023-11-22 15:14:08 +01:00
Write-Host "There is an error encounterd after $attemptTime seconds during package downloading: `n $( $_ . Exception . ToString ()) "
2020-04-21 11:58:27 +03:00
$Retries --
2020-04-02 23:29:22 +03:00
2023-11-22 15:14:08 +01:00
if ( $Retries -eq 0 ) {
Write-Host "Package download failed after $failTime seconds"
2020-04-02 23:29:22 +03:00
exit 1
}
2020-04-21 11:58:27 +03:00
Write-Host "Waiting 30 seconds before retrying. Retries left: $Retries "
2020-04-02 23:29:22 +03:00
Start-Sleep -Seconds 30
}
}
2021-09-20 16:20:56 +03:00
$downloadCompleteTime = [ math ]:: Round (( $ ( Get-Date ) - $downloadStartTime ). TotalSeconds , 2 )
Write-Host "Package downloaded successfully in $downloadCompleteTime seconds"
2020-04-21 11:58:27 +03:00
return $filePath
2020-03-19 02:38:23 +07:00
}
2021-04-23 19:38:43 +03:00
function Get-VsixExtenstionFromMarketplace {
Param
(
[ string ] $ExtensionMarketPlaceName ,
[ string ] $MarketplaceUri = "https://marketplace.visualstudio.com/items?itemName="
)
$extensionUri = $MarketplaceUri + $ExtensionMarketPlaceName
2022-04-20 16:23:32 +04:00
$request = Invoke-SBWithRetry -Command { Invoke-WebRequest -Uri $extensionUri -UseBasicParsing } -RetryCount 20 -RetryIntervalSeconds 30
2021-04-23 19:38:43 +03:00
$request -match 'UniqueIdentifierValue":"(?<extensionname>[^"]*)' | Out-Null
$extensionName = $Matches . extensionname
$request -match 'VsixId":"(?<vsixid>[^"]*)' | Out-Null
$vsixId = $Matches . vsixid
$request -match 'AssetUri":"(?<uri>[^"]*)' | Out-Null
$assetUri = $Matches . uri
$request -match 'Microsoft\.VisualStudio\.Services\.Payload\.FileName":"(?<filename>[^"]*)' | Out-Null
$fileName = $Matches . filename
$downloadUri = $assetUri + "/" + $fileName
2022-08-10 13:55:34 +01:00
# ProBITools.MicrosoftReportProjectsforVisualStudio2022 has different URL https://github.com/actions/runner-images/issues/5340
2022-07-13 16:22:34 +02:00
switch ( $ExtensionMarketPlaceName ) {
"ProBITools.MicrosoftReportProjectsforVisualStudio2022" {
$fileName = "Microsoft.DataTools.ReportingServices.vsix"
$downloadUri = "https://download.microsoft.com/download/b/b/5/bb57be7e-ae72-4fc0-b528-d0ec224997bd/Microsoft.DataTools.ReportingServices.vsix"
}
"ProBITools.MicrosoftAnalysisServicesModelingProjects2022" {
$fileName = "Microsoft.DataTools.AnalysisServices.vsix"
2022-07-20 14:34:03 +02:00
$downloadUri = "https://download.microsoft.com/download/c/8/9/c896a7f2-d0fd-45ac-90e6-ff61f67523cb/Microsoft.DataTools.AnalysisServices.vsix"
2022-07-13 16:22:34 +02:00
}
2022-07-29 22:13:23 +04:00
# Starting from version 4.1 SqlServerIntegrationServicesProjects extension is distributed as exe file
"SSIS.SqlServerIntegrationServicesProjects" {
$fileName = "Microsoft.DataTools.IntegrationServices.exe"
$downloadUri = $assetUri + "/" + $fileName
}
2022-06-01 11:17:05 +03:00
}
2021-04-23 19:38:43 +03:00
return [ PSCustomObject ] @ {
"ExtensionName" = $extensionName
"VsixId" = $vsixId
"FileName" = $fileName
"DownloadUri" = $downloadUri
}
}
2023-11-23 11:58:34 +01:00
function Install-VSIXFromFile {
2020-03-19 02:38:23 +07:00
Param
(
[ Parameter ( Mandatory = $true )]
[ string ] $FilePath ,
2023-11-23 11:58:34 +01:00
[ int ] $Retries = 20
2020-03-19 02:38:23 +07:00
)
2023-11-23 11:58:34 +01:00
Write-Host "Installing VSIX from $FilePath ..."
while ( $True ) {
$installerPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\resources\app\ServiceHub\Services\Microsoft.VisualStudio.Setup.Service\VSIXInstaller.exe"
try {
$process = Start-Process `
-FilePath $installerPath `
-ArgumentList @ ( '/quiet' , " `" $FilePath `" " ) `
-Wait -PassThru
} catch {
Write-Host "Failed to start VSIXInstaller.exe with error:"
2023-01-18 19:51:36 +03:00
$_
exit 1
2021-12-14 11:31:48 +03:00
}
2022-04-20 16:23:32 +04:00
2023-01-18 19:51:36 +03:00
$exitCode = $process . ExitCode
2023-11-23 11:58:34 +01:00
if ( $exitCode -eq 0 ) {
Write-Host "VSIX installed successfully."
break
} elseif ( $exitCode -eq 1001 ) {
Write-Host "VSIX is already installed."
break
2020-03-19 02:38:23 +07:00
}
2023-11-23 11:58:34 +01:00
Write-Host "VSIX installation failed with exit code $exitCode ."
$Retries --
if ( $Retries -eq 0 ) {
Write-Host "VSIX installation failed after $Retries retries."
exit 1
2020-04-21 11:58:27 +03:00
}
2023-11-23 11:58:34 +01:00
Write-Host "Waiting 10 seconds before retrying. Retries left: $Retries "
Start-Sleep -Seconds 10
}
}
function Install-VSIXFromUrl {
Param
(
[ Parameter ( Mandatory = $true )]
[ string ] $Url ,
[ int ] $Retries = 20
)
$name = [ System.IO.Path ]:: GetFileNameWithoutExtension ([ System.IO.Path ]:: GetRandomFileName ()) + ".vsix"
$filePath = Start-DownloadWithRetry -Url $Url -Name $Name
Install-VSIXFromFile -FilePath $filePath -Retries $Retries
Remove-Item -Force -Confirm: $false $filePath
2020-03-19 02:38:23 +07:00
}
2020-09-10 17:04:24 +03:00
function Get-VSExtensionVersion
{
Param
(
[ Parameter ( Mandatory = $true )]
[ string ] $packageName
)
$instanceFolders = Get-ChildItem -Path "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances"
if ( $instanceFolders -is [ array ])
{
2020-12-01 15:56:17 +03:00
Write-Host ( $instanceFolders | Out-String )
Write-Host ( $instanceFolders | Get-ChildItem | Out-String )
2020-09-10 17:04:24 +03:00
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 )
{
2021-04-27 23:03:17 +03:00
Write-Host "Installed package $packageName for Visual Studio was not found"
2020-09-10 17:04:24 +03:00
exit 1
}
return $packageVersion
}
2020-06-30 07:48:55 +03:00
function Get-ToolsetContent
{
2021-08-03 11:56:52 +03:00
$toolsetPath = Join-Path "C:\\image" "toolset.json"
$toolsetJson = Get-Content -Path $toolsetPath -Raw
2020-04-30 18:11:40 +03:00
ConvertFrom-Json -InputObject $toolsetJson
}
2020-07-16 07:30:34 +03:00
function Get-ToolcacheToolDirectory {
Param ([ string ] $ToolName )
$toolcacheRootPath = Resolve-Path $env:AGENT_TOOLSDIRECTORY
return Join-Path $toolcacheRootPath $ToolName
}
2020-06-30 07:48:55 +03:00
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"
)
2020-07-16 07:30:34 +03:00
$toolPath = Get-ToolcacheToolDirectory -ToolName $Name
2020-06-30 07:48:55 +03:00
# Add wildcard if missing
if ( $Version . Split ( "." ). Length -lt 3 ) {
$Version += ".*"
}
2020-07-16 07:30:34 +03:00
$versionPath = Join-Path $toolPath $Version
2020-06-30 07:48:55 +03:00
# Take latest installed version in case if toolset version contains wildcards
2020-07-16 07:30:34 +03:00
$foundVersion = Get-Item $versionPath `
2020-06-30 07:48:55 +03:00
| Sort-Object -Property {[ version ] $_ . name } -Descending `
| Select-Object -First 1
2020-07-16 07:30:34 +03:00
if ( -not $foundVersion ) {
return $null
2020-06-30 07:48:55 +03:00
}
2020-07-16 07:30:34 +03:00
return Join-Path $foundVersion $Arch
2020-06-30 07:48:55 +03:00
}
2020-03-19 02:38:23 +07:00
function Get-WinVersion
{
2020-05-21 19:49:51 +01:00
( Get-CimInstance -ClassName Win32_OperatingSystem ). Caption
2020-03-19 02:38:23 +07:00
}
2021-08-23 11:13:14 +03:00
function Test-IsWin22
{
( Get-WinVersion ) -match "2022"
}
2020-03-19 02:38:23 +07:00
function Test-IsWin19
{
( Get-WinVersion ) -match "2019"
}
2020-05-13 08:02:08 +03:00
function Extract-7Zip {
2020-06-30 07:48:55 +03:00
Param
2020-05-13 08:02:08 +03:00
(
[ Parameter ( Mandatory = $true )]
[ string ] $Path ,
[ Parameter ( Mandatory = $true )]
2023-07-24 09:28:34 +02:00
[ string ] $DestinationPath ,
[ ValidateSet ( "x" , "e" )]
[ char ] $ExtractMethod = "x"
2020-05-13 08:02:08 +03:00
)
Write-Host "Expand archive ' $PATH ' to ' $DestinationPath ' directory"
2023-07-24 09:28:34 +02:00
7z . exe $ExtractMethod " $Path " -o " $DestinationPath " -y | Out-Null
2020-05-13 08:02:08 +03:00
if ( $LASTEXITCODE -ne 0 )
{
Write-Host "There is an error during expanding ' $Path ' to ' $DestinationPath ' directory"
exit 1
}
}
2020-07-17 09:35:46 +00:00
function Install-AndroidSDKPackages {
Param
(
[ Parameter ( Mandatory = $true )]
[ string ] $AndroidSDKManagerPath ,
[ Parameter ( Mandatory = $true )]
[ string ] $AndroidSDKRootPath ,
[ Parameter ( Mandatory = $true )]
2021-08-03 20:17:31 +03:00
[ AllowEmptyCollection ()]
2020-07-17 09:35:46 +00:00
[ string[] ] $AndroidPackages ,
[ string ] $PrefixPackageName
)
foreach ( $package in $AndroidPackages ) {
& $AndroidSDKManagerPath - -sdk_root = $AndroidSDKRootPath " $PrefixPackageName$package "
}
}
2020-09-30 23:42:32 +03:00
function Get-AndroidPackages {
Param
(
[ Parameter ( Mandatory = $true )]
[ string ] $AndroidSDKManagerPath
)
2023-06-16 18:34:04 +02:00
$packagesListFile = "C:\Android\android-sdk\packages-list.txt"
if ( -Not ( Test-Path -Path $packagesListFile -PathType Leaf )) {
( cmd / c " $AndroidSDKManagerPath --list --verbose 2>&1" ) |
Where-Object { $_ -Match "^[^\s]" } |
Where-Object { $_ -NotMatch "^(Loading |Info: Parsing |---|\[=+|Installed |Available )" } |
Where-Object { $_ -NotMatch "^[^;]*$" } |
Out-File -FilePath $packagesListFile
}
return Get-Content $packagesListFile
2020-09-30 23:42:32 +03:00
}
function Get-AndroidPackagesByName {
Param (
[ Parameter ( Mandatory = $true )]
[ string[] ] $AndroidPackages ,
[ Parameter ( Mandatory = $true )]
[ string ] $PrefixPackageName
)
return $AndroidPackages | Where-Object { " $_ " . StartsWith ( $PrefixPackageName ) }
}
function Get-AndroidPackagesByVersion {
Param (
[ Parameter ( Mandatory = $true )]
[ string[] ] $AndroidPackages ,
[ Parameter ( Mandatory = $true )]
[ string ] $PrefixPackageName ,
[ object ] $MinimumVersion ,
2020-09-30 23:47:14 +03:00
[ char ] $Delimiter ,
2020-09-30 23:42:32 +03:00
[ int ] $Index = 0
)
$Type = $MinimumVersion . GetType ()
$packagesByName = Get-AndroidPackagesByName -AndroidPackages $AndroidPackages -PrefixPackageName $PrefixPackageName
2020-09-30 23:47:14 +03:00
$packagesByVersion = $packagesByName | Where-Object { ( $_ . Split ( $Delimiter )[ $Index ] -as $Type ) -ge $MinimumVersion }
2023-03-14 17:14:42 +01:00
return $packagesByVersion | Sort-Object -Unique
2020-09-30 23:42:32 +03:00
}
2021-11-16 17:14:17 +03:00
function Get-WindowsUpdatesHistory {
$allEvents = @ {}
# 19 - Installation Successful: Windows successfully installed the following update
# 20 - Installation Failure: Windows failed to install the following update with error
# 43 - Installation Started: Windows has started installing the following update
$filter = @ {
LogName = "System"
Id = 19 , 20 , 43
ProviderName = "Microsoft-Windows-WindowsUpdateClient"
}
$events = Get-WinEvent -FilterHashtable $filter -ErrorAction SilentlyContinue | Sort-Object Id
foreach ( $event in $events ) {
switch ( $event . Id ) {
19 {
$status = "Successful"
$title = $event . Properties [ 0 ]. Value
$allEvents [ $title ] = ""
break
}
20 {
$status = "Failure"
$title = $event . Properties [ 1 ]. Value
$allEvents [ $title ] = ""
break
}
43 {
$status = "InProgress"
$title = $event . Properties [ 0 ]. Value
2021-12-02 10:56:43 +03:00
break
2021-11-16 17:14:17 +03:00
}
}
if ( $status -eq "InProgress" -and $allEvents . ContainsKey ( $title ) ) {
continue
}
[ PSCustomObject ] @ {
Status = $status
Title = $title
}
}
}
2021-11-26 17:49:05 +03:00
function Invoke-SBWithRetry {
param (
[ scriptblock ] $Command ,
[ int ] $RetryCount = 10 ,
2021-12-02 10:56:43 +03:00
[ int ] $RetryIntervalSeconds = 5
2021-11-26 17:49:05 +03:00
)
while ( $RetryCount -gt 0 ) {
try {
& $Command
return
}
catch {
2021-12-02 10:56:43 +03:00
Write-Host "There is an error encountered: `n $_ "
2021-11-26 17:49:05 +03:00
$RetryCount --
if ( $RetryCount -eq 0 ) {
exit 1
}
2021-12-02 10:56:43 +03:00
Write-Host "Waiting $RetryIntervalSeconds seconds before retrying. Retries left: $RetryCount "
Start-Sleep -Seconds $RetryIntervalSeconds
2021-11-26 17:49:05 +03:00
}
}
}
2021-12-08 10:39:42 +03:00
function Get-GitHubPackageDownloadUrl {
param (
[ string ] $RepoOwner ,
[ string ] $RepoName ,
[ string ] $BinaryName ,
[ string ] $Version ,
[ string ] $UrlFilter ,
[ boolean ] $IsPrerelease = $false ,
2023-10-09 10:55:12 +02:00
[ boolean ] $LatestReleaseOnly = $true ,
2021-12-08 10:39:42 +03:00
[ int ] $SearchInCount = 100
)
2022-04-20 16:23:32 +04:00
if ( $Version -eq "latest" ) {
$Version = "*"
2021-12-08 10:39:42 +03:00
}
2022-06-14 09:34:46 +02:00
2021-12-08 10:39:42 +03:00
$json = Invoke-RestMethod -Uri "https://api.github.com/repos/ ${RepoOwner} / ${RepoName} /releases?per_page= ${SearchInCount} "
2022-06-14 09:34:46 +02:00
$tags = $json . Where { $_ . prerelease -eq $IsPrerelease -and $_ . assets }. tag_name
2023-10-09 10:55:12 +02:00
$availableVersions = $tags |
Select-String -Pattern "\d+.\d+.\d+" |
ForEach-Object { $_ . Matches . Value } |
Where-Object { $_ -like " $Version .*" -or $_ -eq $Version } |
Sort-Object -Descending { [ version ] $_ }
2021-12-08 10:39:42 +03:00
2023-10-09 10:55:12 +02:00
if ( -not $availableVersions ) {
throw "Failed to get available versions from ${RepoOwner} / ${RepoName} releases"
2022-06-14 09:34:46 +02:00
}
2023-10-09 10:55:12 +02:00
if ( $LatestReleaseOnly ) {
$latestVersion = $availableVersions | Select-Object -First 1
$urlFilterReplaced = $UrlFilter -replace "{BinaryName}" , $BinaryName -replace "{Version}" , $latestVersion
$downloadUrl = $json . assets . browser_download_url -like $urlFilterReplaced
} else {
foreach ( $version in $availableVersions ) {
$urlFilterReplaced = $UrlFilter -replace "{BinaryName}" , $BinaryName -replace "{Version}" , $version
$downloadUrl = $json . assets . browser_download_url -like $urlFilterReplaced
if ( $downloadUrl ) {
Write-Host "Found download url for ${RepoOwner} / ${RepoName} ${BinaryName} ${version} "
break
}
}
}
if ( -not $downloadUrl ) {
throw "Failed to get download url for ${RepoOwner} / ${RepoName} ${BinaryName} "
}
2021-12-08 10:39:42 +03:00
return $downloadUrl
}
2023-01-12 23:25:12 +03:00
2023-09-20 17:21:04 +02:00
function Use-ChecksumComparison {
2023-09-18 12:00:27 +02:00
param (
[ Parameter ( Mandatory = $true )]
[ string ] $LocalFileHash ,
[ Parameter ( Mandatory = $true )]
[ string ] $DistributorFileHash
)
Write-Verbose "Performing checksum verification"
if ( $LocalFileHash -ne $DistributorFileHash ) {
throw "Checksum verification failed. Expected hash: $DistributorFileHash ; Actual hash: $LocalFileHash ."
} else {
Write-Verbose "Checksum verification passed"
}
}
function Get-HashFromGitHubReleaseBody {
param (
[ string ] $RepoOwner ,
[ string ] $RepoName ,
[ Parameter ( Mandatory = $true )]
[ string ] $FileName ,
[ string ] $Url ,
[ string ] $Version = "latest" ,
[ boolean ] $IsPrerelease = $false ,
[ int ] $SearchInCount = 100 ,
[ string ] $Delimiter = '|' ,
[ int ] $WordNumber = 1
)
if ( $Url ) {
$releaseUrl = $Url
} else {
if ( $Version -eq "latest" ) {
$releaseUrl = "https://api.github.com/repos/ ${RepoOwner} / ${RepoName} /releases/latest"
} else {
$json = Invoke-RestMethod -Uri "https://api.github.com/repos/ ${RepoOwner} / ${RepoName} /releases?per_page= ${SearchInCount} "
$tags = $json . Where { $_ . prerelease -eq $IsPrerelease }. tag_name
$tag = $tags -match $Version
if ( -not $tag ) {
throw "Failed to get a tag name for version $Version ."
}
$releaseUrl = "https://api.github.com/repos/ ${RepoOwner} / ${RepoName} /releases/tag/ $tag "
}
}
$body = ( Invoke-RestMethod -Uri $releaseUrl ). body -replace ( '`' , "" ) -join " `n "
$matchingLine = $body . Split ( " `n " ) | Where-Object { $_ -like "* $FileName *" }
if ([ string ]:: IsNullOrEmpty ( $matchingLine )) {
throw "File name ' $FileName ' not found in release body."
}
$result = $matchingLine . Split ( $Delimiter )[ $WordNumber ] -replace "[^a-zA-Z0-9]" , ""
if ([ string ]:: IsNullOrEmpty ( $result )) {
throw "Empty result. Check Split method parameters (delimiter and/or word number) for the matching line."
}
return $result
}
2023-09-22 10:52:16 +02:00
function Test-FileSignature {
param (
[ Parameter ( Mandatory = $true )]
[ string ] $FilePath ,
[ Parameter ( Mandatory = $true )]
2023-10-11 11:02:59 +02:00
[ string[] ] $ExpectedThumbprint
2023-09-22 10:52:16 +02:00
)
2023-10-11 11:02:59 +02:00
2023-09-22 10:52:16 +02:00
$signature = Get-AuthenticodeSignature $FilePath
2023-10-11 11:02:59 +02:00
2023-09-22 10:52:16 +02:00
if ( $signature . Status -ne "Valid" ) {
throw "Signature status is not valid. Status: $( $signature . Status ) "
}
2023-10-11 11:02:59 +02:00
foreach ( $thumbprint in $ExpectedThumbprint ) {
if ( $signature . SignerCertificate . Thumbprint . Contains ( $thumbprint )) {
Write-Output "Signature for $FilePath is valid"
$signatureMatched = $true
return
}
2023-09-22 10:52:16 +02:00
}
2023-10-11 11:02:59 +02:00
if ( $signatureMatched ) {
Write-Output "Signature for $FilePath is valid"
}
else {
throw "Signature thumbprint do not match expected."
}
2023-10-09 10:55:12 +02:00
}