From eb0e4ff100b7cd7bf20cc8fbdae059943a2058c2 Mon Sep 17 00:00:00 2001 From: Vasilii Polikarpov <126792224+vpolikarpov-akvelon@users.noreply.github.com> Date: Thu, 30 Nov 2023 16:53:16 +0100 Subject: [PATCH] [Windows] Move Android and VS helpers to dedicated files (#8903) --- .../scripts/build/Install-VSExtensions.ps1 | 5 +- .../docs-gen/SoftwareReport.VisualStudio.psm1 | 26 +- .../scripts/helpers/AndroidHelpers.ps1 | 163 ++++++++++ .../windows/scripts/helpers/ImageHelpers.psm1 | 8 +- .../scripts/helpers/InstallHelpers.ps1 | 294 ------------------ .../scripts/helpers/VisualStudioHelpers.ps1 | 148 ++++++++- .../scripts/tests/VisualStudio.Tests.ps1 | 5 +- images/windows/scripts/tests/Vsix.Tests.ps1 | 12 +- 8 files changed, 332 insertions(+), 329 deletions(-) create mode 100644 images/windows/scripts/helpers/AndroidHelpers.ps1 diff --git a/images/windows/scripts/build/Install-VSExtensions.ps1 b/images/windows/scripts/build/Install-VSExtensions.ps1 index a866876ff..3624d80cf 100644 --- a/images/windows/scripts/build/Install-VSExtensions.ps1 +++ b/images/windows/scripts/build/Install-VSExtensions.ps1 @@ -11,8 +11,9 @@ if (-not $vsixPackagesList) { } $vsixPackagesList | ForEach-Object { - # Retrieve cdn endpoint to avoid HTTP error 429 https://github.com/actions/runner-images/issues/3074 - $vsixPackage = Get-VsixExtenstionFromMarketplace -ExtensionMarketPlaceName $_ + # Retrieve cdn endpoint to avoid HTTP error 429 + # https://github.com/actions/runner-images/issues/3074 + $vsixPackage = Get-VsixInfoFromMarketplace $_ Write-Host "Installing $vsixPackage" if ($vsixPackage.FileName.EndsWith(".vsix")) { Install-VSIXFromUrl $vsixPackage.DownloadUri diff --git a/images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1 b/images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1 index 9a1bdd825..69bad2f02 100644 --- a/images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1 +++ b/images/windows/scripts/docs-gen/SoftwareReport.VisualStudio.psm1 @@ -24,15 +24,13 @@ function Get-VisualStudioExtensions { # Additional vsixs $toolset = Get-ToolsetContent - $vsixUrls = $toolset.visualStudio.vsix - if ($vsixUrls) - { - $vsixs = $vsixUrls | ForEach-Object { - $vsix = Get-VsixExtenstionFromMarketplace -ExtensionMarketPlaceName $_ - - $vsixVersion = ($vsPackages | Where-Object {$_.Id -match $vsix.VsixId -and $_.type -eq 'vsix'}).Version + $vsixPackagesList = $toolset.visualStudio.vsix + if ($vsixPackagesList) { + $vsixs = $vsixPackagesList | ForEach-Object { + $vsixPackage = Get-VsixInfoFromMarketplace $_ + $vsixVersion = ($vsPackages | Where-Object { $_.Id -match $vsixPackage.VsixId -and $_.type -eq 'vsix' }).Version @{ - Package = $vsix.ExtensionName + Package = $vsixPackage.ExtensionName Version = $vsixVersion } } @@ -41,15 +39,15 @@ function Get-VisualStudioExtensions { # SDK $sdkVersion = Get-SDKVersion $sdkPackages = @( - @{Package = 'Windows Software Development Kit'; Version = $sdkVersion} + @{Package = 'Windows Software Development Kit'; Version = $sdkVersion } ) # WDK $wdkVersion = Get-WDKVersion $wdkExtensionVersion = Get-VSExtensionVersion -packageName 'Microsoft.Windows.DriverKit' $wdkPackages = @( - @{Package = 'Windows Driver Kit'; Version = $wdkVersion} - @{Package = 'Windows Driver Kit Visual Studio Extension'; Version = $wdkExtensionVersion} + @{Package = 'Windows Driver Kit'; Version = $wdkVersion } + @{Package = 'Windows Driver Kit Visual Studio Extension'; Version = $wdkExtensionVersion } ) $extensions = @( @@ -60,14 +58,14 @@ function Get-VisualStudioExtensions { ) $extensions | Foreach-Object { - [PSCustomObject]$_ + [PSCustomObject] $_ } | Select-Object Package, Version | Sort-Object Package } function Get-WindowsSDKs { $path = "${env:ProgramFiles(x86)}\Windows Kits\10\Extension SDKs\WindowsDesktop" return [PSCustomObject]@{ - Path = $path + Path = $path Versions = $(Get-ChildItem $path).Name } -} \ No newline at end of file +} diff --git a/images/windows/scripts/helpers/AndroidHelpers.ps1 b/images/windows/scripts/helpers/AndroidHelpers.ps1 new file mode 100644 index 000000000..8ba7a7b70 --- /dev/null +++ b/images/windows/scripts/helpers/AndroidHelpers.ps1 @@ -0,0 +1,163 @@ +function Get-AndroidPackages { + <# + .SYNOPSIS + This function returns a list of available Android packages. + + .DESCRIPTION + The Get-AndroidPackages function checks if a list of packages is already available in a file. + If not, it uses the sdkmanager.bat script to generate a list of available packages and saves it to a file. + It then returns the content of this file. + + .PARAMETER SDKRootPath + The root path of the Android SDK installation. + If not specified, the function uses the ANDROID_HOME environment variable. + + .EXAMPLE + Get-AndroidPackages -SDKRootPath "C:\Android\SDK" + + This command returns a list of available Android packages for the specified SDK root path. + + .NOTES + This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. + + #> + Param + ( + [string] $SDKRootPath + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + $packagesListFile = "$SDKRootPath\packages-list.txt" + $sdkManager = "$SDKRootPath\cmdline-tools\latest\bin\sdkmanager.bat" + + if (-Not (Test-Path -Path $packagesListFile -PathType Leaf)) { + (cmd /c "$sdkManager --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 +} + +function Get-AndroidPlatformPackages { + <# + .SYNOPSIS + This function returns a list of available Android platform packages. + + .DESCRIPTION + The Get-AndroidPlatformPackages function uses the Get-AndroidPackages function to get a list of available packages + and filters it to return only platform packages. + + .PARAMETER SDKRootPath + The root path of the Android SDK installation. + If not specified, the function uses the ANDROID_HOME environment variable. + + .PARAMETER minimumVersion + The minimum version of the platform packages to include in the result. Default is 0. + + .EXAMPLE + Get-AndroidPlatformPackages -SDKRootPath "C:\Android\SDK" -minimumVersion 29 + + This command returns a list of available Android platform packages for the specified SDK root path with a minimum version of 29. + + .NOTES + This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. + + #> + Param + ( + [string] $SDKRootPath, + [Alias("minVersion")] + [int] $minimumVersion = 0 + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + return (Get-AndroidPackages -SDKRootPath $SDKRootPath) ` + | Where-Object { "$_".StartsWith("platforms;") } ` + | Where-Object { ($_.Split("-")[1] -as [int]) -ge $minimumVersion } ` + | Sort-Object -Unique +} + +function Get-AndroidBuildToolPackages { + <# + .SYNOPSIS + This function returns a list of available Android build tool packages. + + .DESCRIPTION + The Get-AndroidBuildToolPackages function uses the Get-AndroidPackages function to get a list of available packages + and filters it to return only build tool packages. + + .PARAMETER SDKRootPath + The root path of the Android SDK installation. + If not specified, the function uses the ANDROID_HOME environment variable. + + .PARAMETER minimumVersion + The minimum version of the build tool packages to include in the result. Default is 0.0.0. + + .EXAMPLE + Get-AndroidBuildToolPackages -SDKRootPath "C:\Android\SDK" -minimumVersion "30.0.2" + + This command returns a list of available Android build tool packages for the specified SDK root path with a minimum version of 30.0.2. + + .NOTES + This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. + + #> + Param + ( + [string] $SDKRootPath, + [Alias("minVersion")] + [version] $minimumVersion = "0.0.0" + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + return (Get-AndroidPackages -SDKRootPath $SDKRootPath) ` + | Where-Object { "$_".StartsWith("build-tools;") } ` + | Where-Object { ($_.Split(";")[1] -as [version]) -ge $minimumVersion } ` + | Sort-Object -Unique +} + +function Get-AndroidInstalledPackages { + <# + .SYNOPSIS + Retrieves a list of installed Android packages. + + .DESCRIPTION + This function retrieves a list of installed Android packages using the specified SDK root path. + + .PARAMETER SDKRootPath + The root path of the Android SDK. + If not specified, the function uses the ANDROID_HOME environment variable. + + .EXAMPLE + Get-AndroidInstalledPackages -SDKRootPath "C:\Android\SDK" + Retrieves a list of installed Android packages using the specified SDK root path. + + .NOTES + This function requires the Android SDK to be installed and the SDK root path to be provided. + #> + + Param + ( + [string] $SDKRootPath + ) + + if (-not $SDKRootPath) { + $SDKRootPath = $env:ANDROID_HOME + } + + $sdkManager = "$SDKRootPath\cmdline-tools\latest\bin\sdkmanager.bat" + + return (cmd /c "$sdkManager --list_installed 2>&1") -notmatch "Warning" +} diff --git a/images/windows/scripts/helpers/ImageHelpers.psm1 b/images/windows/scripts/helpers/ImageHelpers.psm1 index ed65b201a..cad48838a 100644 --- a/images/windows/scripts/helpers/ImageHelpers.psm1 +++ b/images/windows/scripts/helpers/ImageHelpers.psm1 @@ -1,9 +1,10 @@ [CmdletBinding()] param() -. $PSScriptRoot\PathHelpers.ps1 -. $PSScriptRoot\InstallHelpers.ps1 +. $PSScriptRoot\AndroidHelpers.ps1 . $PSScriptRoot\ChocoHelpers.ps1 +. $PSScriptRoot\InstallHelpers.ps1 +. $PSScriptRoot\PathHelpers.ps1 . $PSScriptRoot\VisualStudioHelpers.ps1 Export-ModuleMember -Function @( @@ -17,7 +18,7 @@ Export-ModuleMember -Function @( 'Get-TCToolVersionPath' 'Get-TCToolPath' 'Invoke-DownloadWithRetry' - 'Get-VsixExtenstionFromMarketplace' + 'Get-VsixInfoFromMarketplace' 'Install-VSIXFromFile' 'Install-VSIXFromUrl' 'Get-VSExtensionVersion' @@ -25,7 +26,6 @@ Export-ModuleMember -Function @( 'Test-IsWin22' 'Test-IsWin19' 'Install-ChocoPackage' - 'Send-RequestToCocolateyPackages' 'Resolve-ChocoPackageVersion' 'Resolve-GithubReleaseAssetUrl' 'Expand-7ZipArchive' diff --git a/images/windows/scripts/helpers/InstallHelpers.ps1 b/images/windows/scripts/helpers/InstallHelpers.ps1 index 5a5bef1e8..a892e6571 100644 --- a/images/windows/scripts/helpers/InstallHelpers.ps1 +++ b/images/windows/scripts/helpers/InstallHelpers.ps1 @@ -179,136 +179,6 @@ function Invoke-DownloadWithRetry { return $Path } -function Get-VsixExtenstionFromMarketplace { - Param - ( - [string] $ExtensionMarketPlaceName, - [string] $MarketplaceUri = "https://marketplace.visualstudio.com/items?itemName=" - ) - - # Invoke-WebRequest doesn't support retry in PowerShell 5.1 - $request = Invoke-ScriptBlockWithRetry -RetryCount 20 -RetryIntervalSeconds 30 -Command { - Invoke-WebRequest -Uri "${MarketplaceUri}${ExtensionMarketPlaceName}" -UseBasicParsing - } - $request -match 'UniqueIdentifierValue":"(?[^"]*)' | Out-Null - $extensionName = $Matches.extensionname - $request -match 'VsixId":"(?[^"]*)' | Out-Null - $vsixId = $Matches.vsixid - $request -match 'AssetUri":"(?[^"]*)' | Out-Null - $assetUri = $Matches.uri - $request -match 'Microsoft\.VisualStudio\.Services\.Payload\.FileName":"(?[^"]*)' | Out-Null - $fileName = $Matches.filename - $downloadUri = $assetUri + "/" + $fileName - # ProBITools.MicrosoftReportProjectsforVisualStudio2022 has different URL https://github.com/actions/runner-images/issues/5340 - 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" - $downloadUri = "https://download.microsoft.com/download/c/8/9/c896a7f2-d0fd-45ac-90e6-ff61f67523cb/Microsoft.DataTools.AnalysisServices.vsix" - } - # Starting from version 4.1 SqlServerIntegrationServicesProjects extension is distributed as exe file - "SSIS.SqlServerIntegrationServicesProjects" { - $fileName = "Microsoft.DataTools.IntegrationServices.exe" - $downloadUri = $assetUri + "/" + $fileName - } - } - - return [PSCustomObject] @{ - "ExtensionName" = $extensionName - "VsixId" = $vsixId - "FileName" = $fileName - "DownloadUri" = $downloadUri - } -} - -function Install-VSIXFromFile { - Param - ( - [Parameter(Mandatory = $true)] - [string] $FilePath, - [int] $Retries = 20 - ) - - 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:" - $_ - exit 1 - } - - $exitCode = $process.ExitCode - - if ($exitCode -eq 0) { - Write-Host "VSIX installed successfully." - break - } elseif ($exitCode -eq 1001) { - Write-Host "VSIX is already installed." - break - } - - Write-Host "VSIX installation failed with exit code $exitCode." - - $Retries-- - if ($Retries -eq 0) { - Write-Host "VSIX installation failed after $Retries retries." - exit 1 - } - - 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 - ) - - $filePath = Invoke-DownloadWithRetry $Url - Install-VSIXFromFile -FilePath $filePath -Retries $Retries - 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 ($instanceFolders | Out-String) - Write-Host ($instanceFolders | Get-ChildItem | Out-String) - 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 was not found" - exit 1 - } - - return $packageVersion -} - function Get-ToolsetContent { $toolsetPath = Join-Path "C:\\image" "toolset.json" $toolsetJson = Get-Content -Path $toolsetPath -Raw @@ -428,170 +298,6 @@ function Expand-7ZipArchive { } } -function Get-AndroidPackages { - <# - .SYNOPSIS - This function returns a list of available Android packages. - - .DESCRIPTION - The Get-AndroidPackages function checks if a list of packages is already available in a file. - If not, it uses the sdkmanager.bat script to generate a list of available packages and saves it to a file. - It then returns the content of this file. - - .PARAMETER SDKRootPath - The root path of the Android SDK installation. - If not specified, the function uses the ANDROID_HOME environment variable. - - .EXAMPLE - Get-AndroidPackages -SDKRootPath "C:\Android\SDK" - - This command returns a list of available Android packages for the specified SDK root path. - - .NOTES - This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. - - #> - Param - ( - [string] $SDKRootPath - ) - - if (-not $SDKRootPath) { - $SDKRootPath = $env:ANDROID_HOME - } - - $packagesListFile = "$SDKRootPath\packages-list.txt" - $sdkManager = "$SDKRootPath\cmdline-tools\latest\bin\sdkmanager.bat" - - if (-Not (Test-Path -Path $packagesListFile -PathType Leaf)) { - (cmd /c "$sdkManager --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 -} - -function Get-AndroidPlatformPackages { - <# - .SYNOPSIS - This function returns a list of available Android platform packages. - - .DESCRIPTION - The Get-AndroidPlatformPackages function uses the Get-AndroidPackages function to get a list of available packages - and filters it to return only platform packages. - - .PARAMETER SDKRootPath - The root path of the Android SDK installation. - If not specified, the function uses the ANDROID_HOME environment variable. - - .PARAMETER minimumVersion - The minimum version of the platform packages to include in the result. Default is 0. - - .EXAMPLE - Get-AndroidPlatformPackages -SDKRootPath "C:\Android\SDK" -minimumVersion 29 - - This command returns a list of available Android platform packages for the specified SDK root path with a minimum version of 29. - - .NOTES - This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. - - #> - Param - ( - [string] $SDKRootPath, - [Alias("minVersion")] - [int] $minimumVersion = 0 - ) - - if (-not $SDKRootPath) { - $SDKRootPath = $env:ANDROID_HOME - } - - return (Get-AndroidPackages -SDKRootPath $SDKRootPath) ` - | Where-Object { "$_".StartsWith("platforms;") } ` - | Where-Object { ($_.Split("-")[1] -as [int]) -ge $minimumVersion } ` - | Sort-Object -Unique -} - -function Get-AndroidBuildToolPackages { - <# - .SYNOPSIS - This function returns a list of available Android build tool packages. - - .DESCRIPTION - The Get-AndroidBuildToolPackages function uses the Get-AndroidPackages function to get a list of available packages - and filters it to return only build tool packages. - - .PARAMETER SDKRootPath - The root path of the Android SDK installation. - If not specified, the function uses the ANDROID_HOME environment variable. - - .PARAMETER minimumVersion - The minimum version of the build tool packages to include in the result. Default is 0.0.0. - - .EXAMPLE - Get-AndroidBuildToolPackages -SDKRootPath "C:\Android\SDK" -minimumVersion "30.0.2" - - This command returns a list of available Android build tool packages for the specified SDK root path with a minimum version of 30.0.2. - - .NOTES - This function requires the Android SDK to be installed and the sdkmanager.bat script to be accessible. - - #> - Param - ( - [string] $SDKRootPath, - [Alias("minVersion")] - [version] $minimumVersion = "0.0.0" - ) - - if (-not $SDKRootPath) { - $SDKRootPath = $env:ANDROID_HOME - } - - return (Get-AndroidPackages -SDKRootPath $SDKRootPath) ` - | Where-Object { "$_".StartsWith("build-tools;") } ` - | Where-Object { ($_.Split(";")[1] -as [version]) -ge $minimumVersion } ` - | Sort-Object -Unique -} - -function Get-AndroidInstalledPackages { - <# - .SYNOPSIS - Retrieves a list of installed Android packages. - - .DESCRIPTION - This function retrieves a list of installed Android packages using the specified SDK root path. - - .PARAMETER SDKRootPath - The root path of the Android SDK. - If not specified, the function uses the ANDROID_HOME environment variable. - - .EXAMPLE - Get-AndroidInstalledPackages -SDKRootPath "C:\Android\SDK" - Retrieves a list of installed Android packages using the specified SDK root path. - - .NOTES - This function requires the Android SDK to be installed and the SDK root path to be provided. - #> - - Param - ( - [string] $SDKRootPath - ) - - if (-not $SDKRootPath) { - $SDKRootPath = $env:ANDROID_HOME - } - - $sdkManager = "$SDKRootPath\cmdline-tools\latest\bin\sdkmanager.bat" - - return (cmd /c "$sdkManager --list_installed 2>&1") -notmatch "Warning" -} - function Get-WindowsUpdateStates { <# .SYNOPSIS diff --git a/images/windows/scripts/helpers/VisualStudioHelpers.ps1 b/images/windows/scripts/helpers/VisualStudioHelpers.ps1 index a75baac49..99ef27b4d 100644 --- a/images/windows/scripts/helpers/VisualStudioHelpers.ps1 +++ b/images/windows/scripts/helpers/VisualStudioHelpers.ps1 @@ -116,11 +116,6 @@ Function Install-VisualStudio { } } -function Get-VsCatalogJsonPath { - $instanceFolder = Get-Item "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances\*" | Select-Object -First 1 - return Join-Path $instanceFolder.FullName "catalog.json" -} - function Get-VisualStudioInstance { # Use -Prerelease and -All flags to make sure that Preview versions of VS are found correctly $vsInstance = Get-VSSetupInstance -Prerelease -All | Where-Object { $_.DisplayName -match "Visual Studio" } | Select-Object -First 1 @@ -129,6 +124,145 @@ function Get-VisualStudioInstance { function Get-VisualStudioComponents { (Get-VisualStudioInstance).Packages | Where-Object type -in 'Component', 'Workload' | - Sort-Object Id, Version | Select-Object @{n = 'Package'; e = {$_.Id}}, Version | - Where-Object { $_.Package -notmatch "[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}" } + Sort-Object Id, Version | Select-Object @{n = 'Package'; e = { $_.Id } }, Version | + Where-Object { $_.Package -notmatch "[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}" } +} + +function Get-VsixInfoFromMarketplace { + Param + ( + [Parameter(Mandatory)] + [Alias("ExtensionMarketPlaceName")] + [string] $Name, + [string] $MarketplaceUri = "https://marketplace.visualstudio.com/items?itemName=" + ) + + # Invoke-WebRequest doesn't support retry in PowerShell 5.1 + $webResponse = Invoke-ScriptBlockWithRetry -RetryCount 20 -RetryIntervalSeconds 30 -Command { + Invoke-WebRequest -Uri "${MarketplaceUri}${Name}" -UseBasicParsing + } + + $webResponse -match 'UniqueIdentifierValue":"(?[^"]*)' | Out-Null + $extensionName = $Matches.extensionname + + $webResponse -match 'VsixId":"(?[^"]*)' | Out-Null + $vsixId = $Matches.vsixid + + $webResponse -match 'AssetUri":"(?[^"]*)' | Out-Null + $assetUri = $Matches.uri + + $webResponse -match 'Microsoft\.VisualStudio\.Services\.Payload\.FileName":"(?[^"]*)' | Out-Null + $fileName = $Matches.filename + + switch ($Name) { + # ProBITools.MicrosoftReportProjectsforVisualStudio2022 has different URL + # https://github.com/actions/runner-images/issues/5340 + "ProBITools.MicrosoftReportProjectsforVisualStudio2022" { + $assetUri = "https://download.microsoft.com/download/b/b/5/bb57be7e-ae72-4fc0-b528-d0ec224997bd" + $fileName = "Microsoft.DataTools.ReportingServices.vsix" + } + "ProBITools.MicrosoftAnalysisServicesModelingProjects2022" { + $assetUri = "https://download.microsoft.com/download/c/8/9/c896a7f2-d0fd-45ac-90e6-ff61f67523cb" + $fileName = "Microsoft.DataTools.AnalysisServices.vsix" + } + + # Starting from version 4.1 SqlServerIntegrationServicesProjects extension is distributed as exe file + "SSIS.SqlServerIntegrationServicesProjects" { + $fileName = "Microsoft.DataTools.IntegrationServices.exe" + } + } + + $downloadUri = $assetUri + "/" + $fileName + + return [PSCustomObject] @{ + "ExtensionName" = $extensionName + "VsixId" = $vsixId + "FileName" = $fileName + "DownloadUri" = $downloadUri + } +} + +function Install-VSIXFromFile { + Param + ( + [Parameter(Mandatory = $true)] + [string] $FilePath, + [int] $Retries = 20 + ) + + 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:" + $_ + exit 1 + } + + $exitCode = $process.ExitCode + + if ($exitCode -eq 0) { + Write-Host "VSIX installed successfully." + break + } elseif ($exitCode -eq 1001) { + Write-Host "VSIX is already installed." + break + } + + Write-Host "VSIX installation failed with exit code $exitCode." + + $Retries-- + if ($Retries -eq 0) { + Write-Host "VSIX installation failed after $Retries retries." + exit 1 + } + + 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 + ) + + $filePath = Invoke-DownloadWithRetry $Url + Install-VSIXFromFile -FilePath $filePath -Retries $Retries + 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 ($instanceFolders | Out-String) + Write-Host ($instanceFolders | Get-ChildItem | Out-String) + 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 was not found" + exit 1 + } + + return $packageVersion } diff --git a/images/windows/scripts/tests/VisualStudio.Tests.ps1 b/images/windows/scripts/tests/VisualStudio.Tests.ps1 index 57266fc2c..47b34b17a 100644 --- a/images/windows/scripts/tests/VisualStudio.Tests.ps1 +++ b/images/windows/scripts/tests/VisualStudio.Tests.ps1 @@ -1,7 +1,8 @@ Describe "Visual Studio" { Context "Basic" { It "Catalog.json" { - Get-VsCatalogJsonPath | Should -Exist + $instanceFolder = Get-Item "C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances\*" | Select-Object -First 1 + Join-Path $instanceFolder.FullName "catalog.json" | Should -Exist } It "Devenv.exe" { @@ -34,4 +35,4 @@ Describe "Windows 11 SDK" { It "Verifies 22621 SDK is installed" -Skip:(Test-IsWin22) { "${env:ProgramFiles(x86)}\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.22621.0\UAP.props" | Should -Exist } -} \ No newline at end of file +} diff --git a/images/windows/scripts/tests/Vsix.Tests.ps1 b/images/windows/scripts/tests/Vsix.Tests.ps1 index 5c52da613..28d663681 100644 --- a/images/windows/scripts/tests/Vsix.Tests.ps1 +++ b/images/windows/scripts/tests/Vsix.Tests.ps1 @@ -1,13 +1,13 @@ Describe "Vsix" { $toolset = Get-ToolsetContent - $requiredVsixs = $toolset.visualStudio.vsix + $requiredVsixPackages = $toolset.visualStudio.vsix $allPackages = (Get-VisualStudioInstance).Packages - $testCases = $requiredVsixs | ForEach-Object { - $vsix = Get-VsixExtenstionFromMarketplace -ExtensionMarketPlaceName $_ + $testCases = $requiredVsixPackages | ForEach-Object { + $vsixPackage = Get-VsixInfoFromMarketplace $_ @{ - VsixName = $vsix.ExtensionName - VsixId = $vsix.VsixId + VsixName = $vsixPackage.ExtensionName + VsixId = $vsixPackage.VsixId AllPackages = $allPackages } } @@ -17,4 +17,4 @@ Describe "Vsix" { $objVsix | Should -Not -BeNullOrEmpty } } -} \ No newline at end of file +}