feature: initial commit for all new image changes
This commit is contained in:
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
function Install-ChocoPackage {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A function to install a Chocolatey package with retries.
|
||||
|
||||
.DESCRIPTION
|
||||
This function attempts to install a specified Chocolatey package. If the
|
||||
installation fails, it retries a specified number of times.
|
||||
|
||||
.PARAMETER PackageName
|
||||
The name of the Chocolatey package to install.
|
||||
|
||||
.PARAMETER ArgumentList
|
||||
An array of arguments to pass to the choco install command.
|
||||
|
||||
.PARAMETER RetryCount
|
||||
The number of times to retry the installation if it fails. Default is 5.
|
||||
|
||||
.EXAMPLE
|
||||
Install-ChocoPackage -PackageName "git" -RetryCount 3
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string] $PackageName,
|
||||
[string[]] $ArgumentList,
|
||||
[int] $RetryCount = 5
|
||||
)
|
||||
|
||||
process {
|
||||
$count = 1
|
||||
while ($true) {
|
||||
Write-Host "Running [#$count]: choco install $packageName -y $argumentList"
|
||||
choco install $packageName -y @argumentList --no-progress --require-checksums
|
||||
|
||||
$pkg = choco list --localonly $packageName --exact --all --limitoutput
|
||||
if ($pkg) {
|
||||
Write-Host "Package installed: $pkg"
|
||||
break
|
||||
} else {
|
||||
$count++
|
||||
if ($count -ge $retryCount) {
|
||||
Write-Host "Could not install $packageName after $count attempts"
|
||||
exit 1
|
||||
}
|
||||
Start-Sleep -Seconds 30
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Resolve-ChocoPackageVersion {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Resolves the latest version of a Chocolatey package.
|
||||
|
||||
.DESCRIPTION
|
||||
This function takes a package name and a target version as input and returns the latest
|
||||
version of the package that is greater than or equal to the target version.
|
||||
|
||||
.PARAMETER PackageName
|
||||
The name of the Chocolatey package.
|
||||
|
||||
.PARAMETER TargetVersion
|
||||
The target version of the package.
|
||||
|
||||
.EXAMPLE
|
||||
Resolve-ChocoPackageVersion -PackageName "example-package" -TargetVersion "1.0.0"
|
||||
Returns the latest version of the "example-package" that is greater than or equal to "1.0.0".
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory)]
|
||||
[string] $PackageName,
|
||||
[Parameter(Mandatory)]
|
||||
[string] $TargetVersion
|
||||
)
|
||||
|
||||
$searchResult = choco search $PackageName --exact --all-versions --approved-only --limit-output |
|
||||
ConvertFrom-CSV -Delimiter '|' -Header 'Name', 'Version'
|
||||
|
||||
$latestVersion = $searchResult.Version |
|
||||
Where-Object { $_ -Like "$TargetVersion.*" -or $_ -eq $TargetVersion } |
|
||||
Sort-Object { [version] $_ } |
|
||||
Select-Object -Last 1
|
||||
|
||||
return $latestVersion
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'ImageHelpers.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '0.0.1'
|
||||
|
||||
# Supported PSEditions
|
||||
# CompatiblePSEditions = @()
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = 'c9334909-16a1-48f1-a94a-c7baf1b961d9'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'Helper functions for creating vsts images'
|
||||
|
||||
# Minimum version of the Windows PowerShell engine required by this module
|
||||
# PowerShellVersion = ''
|
||||
|
||||
# Name of the Windows PowerShell host required by this module
|
||||
# PowerShellHostName = ''
|
||||
|
||||
# Minimum version of the Windows PowerShell host required by this module
|
||||
# PowerShellHostVersion = ''
|
||||
|
||||
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# DotNetFrameworkVersion = ''
|
||||
|
||||
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
|
||||
# CLRVersion = ''
|
||||
|
||||
# Processor architecture (None, X86, Amd64) required by this module
|
||||
# ProcessorArchitecture = ''
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
# RequiredModules = @()
|
||||
|
||||
# Assemblies that must be loaded prior to importing this module
|
||||
# RequiredAssemblies = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
# ScriptsToProcess = @()
|
||||
|
||||
# Type files (.ps1xml) to be loaded when importing this module
|
||||
# TypesToProcess = @()
|
||||
|
||||
# Format files (.ps1xml) to be loaded when importing this module
|
||||
# FormatsToProcess = @()
|
||||
|
||||
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
|
||||
# NestedModules = @()
|
||||
|
||||
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
|
||||
FunctionsToExport = '*'
|
||||
|
||||
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
|
||||
CmdletsToExport = '*'
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = '*'
|
||||
|
||||
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
|
||||
AliasesToExport = '*'
|
||||
|
||||
# DSC resources to export from this module
|
||||
# DscResourcesToExport = @()
|
||||
|
||||
# List of all modules packaged with this module
|
||||
# ModuleList = @()
|
||||
|
||||
# List of all files packaged with this module
|
||||
# FileList = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
|
||||
PrivateData = @{
|
||||
|
||||
PSData = @{
|
||||
|
||||
# Tags applied to this module. These help with module discovery in online galleries.
|
||||
# Tags = @()
|
||||
|
||||
# A URL to the license for this module.
|
||||
# LicenseUri = ''
|
||||
|
||||
# A URL to the main website for this project.
|
||||
# ProjectUri = ''
|
||||
|
||||
# A URL to an icon representing this module.
|
||||
# IconUri = ''
|
||||
|
||||
# ReleaseNotes of this module
|
||||
# ReleaseNotes = ''
|
||||
|
||||
} # End of PSData hashtable
|
||||
|
||||
} # End of PrivateData hashtable
|
||||
|
||||
# HelpInfo URI of this module
|
||||
# HelpInfoURI = ''
|
||||
|
||||
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
|
||||
# DefaultCommandPrefix = ''
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
. $PSScriptRoot\AndroidHelpers.ps1
|
||||
Export-ModuleMember -Function @(
|
||||
'Get-AndroidPackages'
|
||||
'Get-AndroidPlatformPackages'
|
||||
'Get-AndroidBuildToolPackages'
|
||||
'Get-AndroidInstalledPackages'
|
||||
)
|
||||
|
||||
. $PSScriptRoot\ChocoHelpers.ps1
|
||||
Export-ModuleMember -Function @(
|
||||
'Install-ChocoPackage'
|
||||
'Resolve-ChocoPackageVersion'
|
||||
)
|
||||
|
||||
. $PSScriptRoot\InstallHelpers.ps1
|
||||
Export-ModuleMember -Function @(
|
||||
'Install-Binary'
|
||||
'Invoke-DownloadWithRetry'
|
||||
'Get-ToolsetContent'
|
||||
'Get-TCToolPath'
|
||||
'Get-TCToolVersionPath'
|
||||
'Test-IsWin25'
|
||||
'Test-IsWin22'
|
||||
'Test-IsWin19'
|
||||
'Expand-7ZipArchive'
|
||||
'Get-WindowsUpdateStates'
|
||||
'Invoke-ScriptBlockWithRetry'
|
||||
'Get-GithubReleasesByVersion'
|
||||
'Resolve-GithubReleaseAssetUrl'
|
||||
'Get-ChecksumFromGithubRelease'
|
||||
'Get-ChecksumFromUrl'
|
||||
'Test-FileChecksum'
|
||||
'Test-FileSignature'
|
||||
'Update-Environment'
|
||||
)
|
||||
|
||||
. $PSScriptRoot\PathHelpers.ps1
|
||||
Export-ModuleMember -Function @(
|
||||
'Mount-RegistryHive'
|
||||
'Dismount-RegistryHive'
|
||||
'Add-MachinePathItem'
|
||||
'Add-DefaultPathItem'
|
||||
)
|
||||
|
||||
. $PSScriptRoot\VisualStudioHelpers.ps1
|
||||
Export-ModuleMember -Function @(
|
||||
'Install-VisualStudio'
|
||||
'Get-VisualStudioInstance'
|
||||
'Get-VisualStudioComponents'
|
||||
'Get-VsixInfoFromMarketplace'
|
||||
'Install-VSIXFromFile'
|
||||
'Install-VSIXFromUrl'
|
||||
'Get-VSExtensionVersion'
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,137 @@
|
||||
|
||||
function Mount-RegistryHive {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Mounts a registry hive from a file.
|
||||
|
||||
.DESCRIPTION
|
||||
The Mount-RegistryHive function loads a registry hive from a specified file into a specified subkey.
|
||||
|
||||
.PARAMETER FileName
|
||||
The path to the file from which to load the registry hive.
|
||||
|
||||
.PARAMETER SubKey
|
||||
The registry subkey into which to load the hive.
|
||||
|
||||
.EXAMPLE
|
||||
Mount-RegistryHive -FileName "C:\Path\To\HiveFile.hiv" -SubKey "HKLM\SubKey"
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $FileName,
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $SubKey
|
||||
)
|
||||
|
||||
Write-Host "Loading the file $FileName to the Key $SubKey"
|
||||
if (Test-Path $SubKey.Replace("\", ":")) {
|
||||
Write-Warning "The key $SubKey is already loaded"
|
||||
return
|
||||
}
|
||||
|
||||
$result = reg load $SubKey $FileName *>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Failed to load file $FileName to the key ${SubKey}: $result"
|
||||
}
|
||||
}
|
||||
|
||||
function Dismount-RegistryHive {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Dismounts a registry hive.
|
||||
|
||||
.DESCRIPTION
|
||||
The Dismount-RegistryHive function unloads a registry hive from a specified subkey.
|
||||
|
||||
.PARAMETER SubKey
|
||||
The registry subkey from which to unload the hive.
|
||||
|
||||
.EXAMPLE
|
||||
Dismount-RegistryHive -SubKey "HKLM\SubKey"
|
||||
#>
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $SubKey
|
||||
)
|
||||
|
||||
Write-Host "Unloading the hive $SubKey"
|
||||
if (-not (Test-Path $SubKey.Replace("\", ":"))) {
|
||||
return
|
||||
}
|
||||
|
||||
$result = reg unload $SubKey *>&1
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host "Failed to unload hive: $result"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
function Add-MachinePathItem {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Adds a new item to the machine-level PATH environment variable.
|
||||
|
||||
.DESCRIPTION
|
||||
The Add-MachinePathItem function adds a new item to the machine-level PATH environment variable.
|
||||
It takes a string parameter, $PathItem, which represents the new item to be added to the PATH.
|
||||
|
||||
.PARAMETER PathItem
|
||||
Specifies the new item to be added to the machine-level PATH environment variable.
|
||||
|
||||
.EXAMPLE
|
||||
Add-MachinePathItem -PathItem "C:\Program Files\MyApp"
|
||||
|
||||
This example adds "C:\Program Files\MyApp" to the machine-level PATH environment variable.
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $PathItem
|
||||
)
|
||||
|
||||
$currentPath = [System.Environment]::GetEnvironmentVariable("PATH", "Machine")
|
||||
$newPath = $PathItem + ';' + $currentPath
|
||||
[Environment]::SetEnvironmentVariable("PATH", $newPath, "Machine")
|
||||
}
|
||||
|
||||
function Add-DefaultPathItem {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Adds a path item to the default user profile path.
|
||||
|
||||
.DESCRIPTION
|
||||
This function adds a specified path item to the default user profile path.
|
||||
It mounts the NTUSER.DAT file of the default user to the registry,
|
||||
retrieves the current value of the "Path" environment variable,
|
||||
appends the new path item to it, and updates the registry with the modified value.
|
||||
|
||||
.PARAMETER PathItem
|
||||
The path item to be added to the default user profile path.
|
||||
|
||||
.EXAMPLE
|
||||
Add-DefaultPathItem -PathItem "C:\Program Files\MyApp"
|
||||
|
||||
This example adds "C:\Program Files\MyApp" to the default user profile path.
|
||||
|
||||
.NOTES
|
||||
This function requires administrative privileges to modify the Windows registry.
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $PathItem
|
||||
)
|
||||
|
||||
Mount-RegistryHive `
|
||||
-FileName "C:\Users\Default\NTUSER.DAT" `
|
||||
-SubKey "HKLM\DEFAULT"
|
||||
|
||||
$key = [Microsoft.Win32.Registry]::LocalMachine.OpenSubKey("DEFAULT\Environment", $true)
|
||||
$currentValue = $key.GetValue("Path", "", "DoNotExpandEnvironmentNames")
|
||||
$updatedValue = $PathItem + ';' + $currentValue
|
||||
$key.SetValue("Path", $updatedValue, "ExpandString")
|
||||
$key.Handle.Close()
|
||||
[System.GC]::Collect()
|
||||
|
||||
Dismount-RegistryHive "HKLM\DEFAULT"
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
Function Install-VisualStudio {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
A helper function to install Visual Studio.
|
||||
|
||||
.DESCRIPTION
|
||||
Prepare system environment, and install Visual Studio bootstrapper with selected workloads.
|
||||
|
||||
.PARAMETER Version
|
||||
The version of Visual Studio that will be installed. Required parameter.
|
||||
|
||||
.PARAMETER Edition
|
||||
The edition of Visual Studio that will be installed. Required parameter.
|
||||
|
||||
.PARAMETER Channel
|
||||
The channel of Visual Studio that will be installed. Required parameter.
|
||||
|
||||
.PARAMETER RequiredComponents
|
||||
The list of required components. Required parameter.
|
||||
|
||||
.PARAMETER ExtraArgs
|
||||
The extra arguments to pass to the bootstrapper. Optional parameter.
|
||||
#>
|
||||
|
||||
Param
|
||||
(
|
||||
[Parameter(Mandatory)] [String] $Version,
|
||||
[Parameter(Mandatory)] [String] $Edition,
|
||||
[Parameter(Mandatory)] [String] $Channel,
|
||||
[Parameter(Mandatory)] [String[]] $RequiredComponents,
|
||||
[String] $ExtraArgs = "",
|
||||
[Parameter(Mandatory)] [String[]] $SignatureThumbprint
|
||||
)
|
||||
|
||||
$bootstrapperUrl = "https://aka.ms/vs/${Version}/${Channel}/vs_${Edition}.exe"
|
||||
$channelUri = "https://aka.ms/vs/${Version}/${Channel}/channel"
|
||||
$channelId = "VisualStudio.${Version}.Release"
|
||||
$productId = "Microsoft.VisualStudio.Product.${Edition}"
|
||||
|
||||
Write-Host "Downloading Bootstrapper ..."
|
||||
$bootstrapperFilePath = Invoke-DownloadWithRetry $BootstrapperUrl
|
||||
|
||||
# Verify that the bootstrapper is signed by Microsoft
|
||||
Test-FileSignature -Path $bootstrapperFilePath -ExpectedThumbprint $SignatureThumbprint
|
||||
|
||||
try {
|
||||
$responseData = @{
|
||||
"channelUri" = $channelUri
|
||||
"channelId" = $channelId
|
||||
"productId" = $productId
|
||||
"arch" = "x64"
|
||||
"add" = $RequiredComponents | ForEach-Object { "$_;includeRecommended" }
|
||||
}
|
||||
|
||||
# Create json file with response data
|
||||
$responseDataPath = "$env:TEMP\vs_install_response.json"
|
||||
$responseData | ConvertTo-Json | Out-File -FilePath $responseDataPath
|
||||
|
||||
$installStartTime = Get-Date
|
||||
Write-Host "Starting Install ..."
|
||||
$bootstrapperArgumentList = ('/c', $bootstrapperFilePath, '--in', $responseDataPath, $ExtraArgs, '--quiet', '--norestart', '--wait', '--nocache' )
|
||||
Write-Host "Bootstrapper arguments: $bootstrapperArgumentList"
|
||||
$process = Start-Process -FilePath cmd.exe -ArgumentList $bootstrapperArgumentList -Wait -PassThru
|
||||
|
||||
$exitCode = $process.ExitCode
|
||||
$installCompleteTime = [math]::Round(($(Get-Date) - $installStartTime).TotalSeconds, 2)
|
||||
if ($exitCode -eq 0) {
|
||||
Write-Host "Installation successful in $installCompleteTime seconds"
|
||||
return $exitCode
|
||||
} elseif ($exitCode -eq 3010) {
|
||||
Write-Host "Installation successful in $installCompleteTime seconds. Reboot is required."
|
||||
return $exitCode
|
||||
} else {
|
||||
Write-Host "Non zero exit code returned by the installation process : $exitCode"
|
||||
|
||||
# Try to download tool to collect logs
|
||||
$collectExeUrl = "https://aka.ms/vscollect.exe"
|
||||
$collectExePath = Invoke-DownloadWithRetry -Url $collectExeUrl
|
||||
|
||||
# Collect installation logs using the collect.exe tool and check if it is successful
|
||||
& "$collectExePath"
|
||||
if ($LastExitCode -ne 0) {
|
||||
Write-Host "Failed to collect logs using collect.exe tool. Exit code : $LastExitCode"
|
||||
exit $exitCode
|
||||
}
|
||||
|
||||
# Expand the zip file
|
||||
Expand-Archive -Path "$env:TEMP\vslogs.zip" -DestinationPath "$env:TEMP\vslogs"
|
||||
|
||||
# Print logs
|
||||
$vsLogsPath = "$env:TEMP\vslogs"
|
||||
$vsLogs = Get-ChildItem -Path $vsLogsPath -Recurse | Where-Object { -not $_.PSIsContainer } | Select-Object -ExpandProperty FullName
|
||||
foreach ($log in $vsLogs) {
|
||||
Write-Host "============================"
|
||||
Write-Host "== Log file : $log "
|
||||
Write-Host "============================"
|
||||
Get-Content -Path $log -ErrorAction Continue
|
||||
}
|
||||
|
||||
exit $exitCode
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Write-Host "Failed to install Visual Studio; $($_.Exception.Message)"
|
||||
exit -1
|
||||
}
|
||||
}
|
||||
|
||||
function Get-VisualStudioInstance {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Retrieves the Visual Studio instance.
|
||||
|
||||
.DESCRIPTION
|
||||
This function retrieves the Visual Studio instance
|
||||
using the Get-VSSetupInstance cmdlet.
|
||||
It searches for both regular and preview versions
|
||||
of Visual Studio and returns the first instance found.
|
||||
#>
|
||||
|
||||
# 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
|
||||
$vsInstance | Select-VSSetupInstance -Product *
|
||||
}
|
||||
|
||||
function Get-VisualStudioComponents {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Retrieves the Visual Studio components.
|
||||
|
||||
.DESCRIPTION
|
||||
This function retrieves the Visual Studio components
|
||||
by filtering the packages returned by Get-VisualStudioInstance cmdlet.
|
||||
It filters the packages based on their type, sorts them by Id and Version,
|
||||
and excludes packages with GUID-like Ids.
|
||||
#>
|
||||
|
||||
(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}" }
|
||||
}
|
||||
|
||||
function Get-VsixInfoFromMarketplace {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Retrieves information about a Visual Studio extension from the Visual Studio Marketplace.
|
||||
|
||||
.DESCRIPTION
|
||||
The Get-VsixInfoFromMarketplace function retrieves information
|
||||
about a Visual Studio extension from the Visual Studio Marketplace.
|
||||
It takes the name of the extension as input and returns
|
||||
the extension's name, VsixId, filename, and download URI.
|
||||
|
||||
.PARAMETER Name
|
||||
The name of the Visual Studio extension.
|
||||
|
||||
.PARAMETER MarketplaceUri
|
||||
The URI of the Visual Studio Marketplace.
|
||||
Default value is "https://marketplace.visualstudio.com/items?itemName=".
|
||||
|
||||
.EXAMPLE
|
||||
Get-VsixInfoFromMarketplace -Name "ProBITools.MicrosoftReportProjectsforVisualStudio2022"
|
||||
Retrieves information about the "ProBITools.MicrosoftReportProjectsforVisualStudio2022" extension
|
||||
from the Visual Studio Marketplace.
|
||||
#>
|
||||
|
||||
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":"(?<extensionname>[^"]*)' | Out-Null
|
||||
$extensionName = $Matches.extensionname
|
||||
|
||||
$webResponse -match 'VsixId":"(?<vsixid>[^"]*)' | Out-Null
|
||||
$vsixId = $Matches.vsixid
|
||||
|
||||
$webResponse -match 'AssetUri":"(?<uri>[^"]*)' | Out-Null
|
||||
$assetUri = $Matches.uri
|
||||
|
||||
$webResponse -match 'Microsoft\.VisualStudio\.Services\.Payload\.FileName":"(?<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 {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Installs a Visual Studio Extension (VSIX) from a file.
|
||||
|
||||
.DESCRIPTION
|
||||
This function installs a Visual Studio Extension (VSIX)
|
||||
from the specified file path. It uses the VSIXInstaller.exe
|
||||
tool provided by Microsoft Visual Studio.
|
||||
|
||||
.PARAMETER FilePath
|
||||
The path to the VSIX file that needs to be installed.
|
||||
|
||||
.PARAMETER Retries
|
||||
The number of retries to attempt if the installation fails. Default is 20.
|
||||
|
||||
.EXAMPLE
|
||||
Install-VSIXFromFile -FilePath "C:\Extensions\MyExtension.vsix" -Retries 10
|
||||
Installs the VSIX file located at "C:\Extensions\MyExtension.vsix" with 10 retries in case of failure.
|
||||
#>
|
||||
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 {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Installs a Visual Studio extension (VSIX) from a given URL.
|
||||
|
||||
.DESCRIPTION
|
||||
This function downloads a Visual Studio extension (VSIX)
|
||||
from the specified URL and installs it.
|
||||
|
||||
.PARAMETER Url
|
||||
The URL of the VSIX file to download and install.
|
||||
|
||||
.PARAMETER Retries
|
||||
The number of retries to attempt if the download fails. Default is 20.
|
||||
|
||||
.EXAMPLE
|
||||
Install-VSIXFromUrl -Url "https://example.com/extension.vsix" -Retries 10
|
||||
Downloads and installs the VSIX file from the specified URL with 10 retries.
|
||||
#>
|
||||
|
||||
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 {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Retrieves the version of a Visual Studio extension package.
|
||||
|
||||
.DESCRIPTION
|
||||
This function retrieves the version of a specified Visual Studio extension package.
|
||||
It searches for the package in the installed instances of Visual Studio and
|
||||
returns the version number.
|
||||
|
||||
.PARAMETER packageName
|
||||
The name of the extension package.
|
||||
|
||||
.EXAMPLE
|
||||
Get-VSExtensionVersion -packageName "MyExtensionPackage"
|
||||
Retrieves the version of the extension package named "MyExtensionPackage" for Visual Studio.
|
||||
#>
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
$ModuleManifestName = 'ImageHelpers.psd1'
|
||||
$ModuleManifestPath = "$PSScriptRoot\..\$ModuleManifestName"
|
||||
|
||||
|
||||
|
||||
Describe 'Module Manifest Tests' {
|
||||
It 'Passes Test-ModuleManifest' {
|
||||
Test-ModuleManifest -Path $ModuleManifestPath | Should Not BeNullOrEmpty
|
||||
$? | Should Be $true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user