new cdn url changes

This commit is contained in:
HarithaVattikuti 2024-12-25 19:37:39 -06:00
parent a351d9ea84
commit 94bfe42e69
2 changed files with 1678 additions and 1532 deletions

View File

@ -9,20 +9,27 @@
.DESCRIPTION .DESCRIPTION
Installs dotnet cli. If dotnet installation already exists in the given directory Installs dotnet cli. If dotnet installation already exists in the given directory
it will update it only if the requested version differs from the one already installed. it will update it only if the requested version differs from the one already installed.
Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:
- The SDK needs to be installed without user interaction and without admin rights.
- The SDK installation doesn't need to persist across multiple CI runs.
To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.
.PARAMETER Channel .PARAMETER Channel
Default: LTS Default: LTS
Download from the Channel specified. Possible values: Download from the Channel specified. Possible values:
- Current - most current release - STS - the most recent Standard Term Support release
- LTS - most current supported release - LTS - the most recent Long Term Support release
- 2-part version in a format A.B - represents a specific release - 2-part version in a format A.B - represents a specific release
examples: 2.0, 1.0 examples: 2.0, 1.0
- 3-part version in a format A.B.Cxx - represents a specific SDK release - 3-part version in a format A.B.Cxx - represents a specific SDK release
examples: 5.0.1xx, 5.0.2xx examples: 5.0.1xx, 5.0.2xx
Supported since 5.0 release Supported since 5.0 release
Warning: Value "Current" is deprecated for the Channel parameter. Use "STS" instead.
Note: The version parameter overrides the channel parameter when any version other than 'latest' is used. Note: The version parameter overrides the channel parameter when any version other than 'latest' is used.
.PARAMETER Quality .PARAMETER Quality
Download the latest build of specified quality in the channel. The possible values are: daily, signed, validated, preview, GA. Download the latest build of specified quality in the channel. The possible values are: daily, signed, validated, preview, GA.
Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used. Works only in combination with channel. Not applicable for STS and LTS channels and will be ignored if those channels are used.
For SDK use channel in A.B.Cxx format: using quality together with channel in A.B format is not supported. For SDK use channel in A.B.Cxx format: using quality together with channel in A.B format is not supported.
Supported since 5.0 release. Supported since 5.0 release.
Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality. Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality.
@ -91,16 +98,26 @@
.PARAMETER DownloadTimeout .PARAMETER DownloadTimeout
Determines timeout duration in seconds for dowloading of the SDK file Determines timeout duration in seconds for dowloading of the SDK file
Default: 1200 seconds (20 minutes) Default: 1200 seconds (20 minutes)
.PARAMETER KeepZip
If set, downloaded file is kept
.PARAMETER ZipPath
Use that path to store installer, generated by default
.EXAMPLE
dotnet-install.ps1 -Version 7.0.401
Installs the .NET SDK version 7.0.401
.EXAMPLE
dotnet-install.ps1 -Channel 8.0 -Quality GA
Installs the latest GA (general availability) version of the .NET 8.0 SDK
#> #>
[cmdletbinding()] [cmdletbinding()]
param( param(
[string]$Channel="LTS", [string]$Channel = "LTS",
[string]$Quality, [string]$Quality,
[string]$Version="Latest", [string]$Version = "Latest",
[switch]$Internal, [switch]$Internal,
[string]$JSonFile, [string]$JSonFile,
[Alias('i')][string]$InstallDir="<auto>", [Alias('i')][string]$InstallDir = "<auto>",
[string]$Architecture="<auto>", [string]$Architecture = "<auto>",
[string]$Runtime, [string]$Runtime,
[Obsolete("This parameter may be removed in a future version of this script. The recommended alternative is '-Runtime dotnet'.")] [Obsolete("This parameter may be removed in a future version of this script. The recommended alternative is '-Runtime dotnet'.")]
[switch]$SharedRuntime, [switch]$SharedRuntime,
@ -111,15 +128,18 @@ param(
[string]$FeedCredential, [string]$FeedCredential,
[string]$ProxyAddress, [string]$ProxyAddress,
[switch]$ProxyUseDefaultCredentials, [switch]$ProxyUseDefaultCredentials,
[string[]]$ProxyBypassList=@(), [string[]]$ProxyBypassList = @(),
[switch]$SkipNonVersionedFiles, [switch]$SkipNonVersionedFiles,
[switch]$NoCdn, [switch]$NoCdn,
[int]$DownloadTimeout=1200 [int]$DownloadTimeout = 1200,
[switch]$KeepZip,
[string]$ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName()),
[switch]$Help
) )
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference="Stop" $ErrorActionPreference = "Stop"
$ProgressPreference="SilentlyContinue" $ProgressPreference = "SilentlyContinue"
function Say($str) { function Say($str) {
try { try {
@ -163,6 +183,29 @@ function Say-Verbose($str) {
} }
} }
function Measure-Action($name, $block) {
$time = Measure-Command $block
$totalSeconds = $time.TotalSeconds
Say-Verbose "Action '$name' took $totalSeconds seconds"
}
function Get-Remote-File-Size($zipUri) {
try {
$response = Invoke-WebRequest -Uri $zipUri -Method Head
$fileSize = $response.Headers["Content-Length"]
if ((![string]::IsNullOrEmpty($fileSize))) {
Say "Remote file $zipUri size is $fileSize bytes."
return $fileSize
}
}
catch {
Say-Verbose "Content-Length header was not extracted for $zipUri."
}
return $null
}
function Say-Invocation($Invocation) { function Say-Invocation($Invocation) {
$command = $Invocation.MyCommand; $command = $Invocation.MyCommand;
$args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ") $args = (($Invocation.BoundParameters.Keys | foreach { "-$_ `"$($Invocation.BoundParameters[$_])`"" }) -join " ")
@ -200,14 +243,13 @@ function Get-Machine-Architecture() {
# To get the correct architecture, we need to use PROCESSOR_ARCHITEW6432. # To get the correct architecture, we need to use PROCESSOR_ARCHITEW6432.
# PS x64 doesn't define this, so we fall back to PROCESSOR_ARCHITECTURE. # PS x64 doesn't define this, so we fall back to PROCESSOR_ARCHITECTURE.
# Possible values: amd64, x64, x86, arm64, arm # Possible values: amd64, x64, x86, arm64, arm
if( $ENV:PROCESSOR_ARCHITEW6432 -ne $null ) { if ( $ENV:PROCESSOR_ARCHITEW6432 -ne $null ) {
return $ENV:PROCESSOR_ARCHITEW6432 return $ENV:PROCESSOR_ARCHITEW6432
} }
try { try {
if( ((Get-CimInstance -ClassName CIM_OperatingSystem).OSArchitecture) -like "ARM*") { if ( ((Get-CimInstance -ClassName CIM_OperatingSystem).OSArchitecture) -like "ARM*") {
if( [Environment]::Is64BitOperatingSystem ) if ( [Environment]::Is64BitOperatingSystem ) {
{
return "arm64" return "arm64"
} }
return "arm" return "arm"
@ -236,13 +278,13 @@ function Get-CLIArchitecture-From-Architecture([string]$Architecture) {
} }
} }
function ValidateFeedCredential([string] $FeedCredential) function ValidateFeedCredential([string] $FeedCredential) {
{
if ($Internal -and [string]::IsNullOrWhitespace($FeedCredential)) { if ($Internal -and [string]::IsNullOrWhitespace($FeedCredential)) {
$message = "Provide credentials via -FeedCredential parameter." $message = "Provide credentials via -FeedCredential parameter."
if ($DryRun) { if ($DryRun) {
Say-Warning "$message" Say-Warning "$message"
} else { }
else {
throw "$message" throw "$message"
} }
} }
@ -277,13 +319,18 @@ function Get-NormalizedChannel([string]$Channel) {
return "" return ""
} }
if ($Channel.Contains("Current")) {
Say-Warning 'Value "Current" is deprecated for -Channel option. Use "STS" instead.'
}
if ($Channel.StartsWith('release/')) { if ($Channel.StartsWith('release/')) {
Say-Warning 'Using branch name with -Channel option is no longer supported with newer releases. Use -Quality option with a channel in X.Y format instead, such as "-Channel 5.0 -Quality Daily."' Say-Warning 'Using branch name with -Channel option is no longer supported with newer releases. Use -Quality option with a channel in X.Y format instead, such as "-Channel 5.0 -Quality Daily."'
} }
switch ($Channel) { switch ($Channel) {
{ $_ -eq "lts" } { return "LTS" } { $_ -eq "lts" } { return "LTS" }
{ $_ -eq "current" } { return "current" } { $_ -eq "sts" } { return "STS" }
{ $_ -eq "current" } { return "STS" }
default { return $Channel.ToLowerInvariant() } default { return $Channel.ToLowerInvariant() }
} }
} }
@ -329,8 +376,7 @@ function Load-Assembly([string] $Assembly) {
} }
} }
function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect, [bool]$DisableFeedCredential) function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect, [bool]$DisableFeedCredential) {
{
$cts = New-Object System.Threading.CancellationTokenSource $cts = New-Object System.Threading.CancellationTokenSource
$downloadScript = { $downloadScript = {
@ -341,19 +387,21 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
# HttpClient is used vs Invoke-WebRequest in order to support Nano Server which doesn't support the Invoke-WebRequest cmdlet. # HttpClient is used vs Invoke-WebRequest in order to support Nano Server which doesn't support the Invoke-WebRequest cmdlet.
Load-Assembly -Assembly System.Net.Http Load-Assembly -Assembly System.Net.Http
if(-not $ProxyAddress) { if (-not $ProxyAddress) {
try { try {
# Despite no proxy being explicitly specified, we may still be behind a default proxy # Despite no proxy being explicitly specified, we may still be behind a default proxy
$DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy; $DefaultProxy = [System.Net.WebRequest]::DefaultWebProxy;
if($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))) { if ($DefaultProxy -and (-not $DefaultProxy.IsBypassed($Uri))) {
if ($null -ne $DefaultProxy.GetProxy($Uri)) { if ($null -ne $DefaultProxy.GetProxy($Uri)) {
$ProxyAddress = $DefaultProxy.GetProxy($Uri).OriginalString $ProxyAddress = $DefaultProxy.GetProxy($Uri).OriginalString
} else { }
else {
$ProxyAddress = $null $ProxyAddress = $null
} }
$ProxyUseDefaultCredentials = $true $ProxyUseDefaultCredentials = $true
} }
} catch { }
catch {
# Eat the exception and move forward as the above code is an attempt # Eat the exception and move forward as the above code is an attempt
# at resolving the DefaultProxy that may not have been a problem. # at resolving the DefaultProxy that may not have been a problem.
$ProxyAddress = $null $ProxyAddress = $null
@ -362,15 +410,14 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
} }
$HttpClientHandler = New-Object System.Net.Http.HttpClientHandler $HttpClientHandler = New-Object System.Net.Http.HttpClientHandler
if($ProxyAddress) { if ($ProxyAddress) {
$HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{ $HttpClientHandler.Proxy = New-Object System.Net.WebProxy -Property @{
Address=$ProxyAddress; Address = $ProxyAddress;
UseDefaultCredentials=$ProxyUseDefaultCredentials; UseDefaultCredentials = $ProxyUseDefaultCredentials;
BypassList = $ProxyBypassList; BypassList = $ProxyBypassList;
} }
} }
if ($DisableRedirect) if ($DisableRedirect) {
{
$HttpClientHandler.AllowAutoRedirect = $false $HttpClientHandler.AllowAutoRedirect = $false
} }
$HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler $HttpClient = New-Object System.Net.Http.HttpClient -ArgumentList $HttpClientHandler
@ -379,7 +426,7 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
# Defaulting to 20 minutes allows it to work over much slower connections. # Defaulting to 20 minutes allows it to work over much slower connections.
$HttpClient.Timeout = New-TimeSpan -Seconds $DownloadTimeout $HttpClient.Timeout = New-TimeSpan -Seconds $DownloadTimeout
if ($HeaderOnly){ if ($HeaderOnly) {
$completionOption = [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead $completionOption = [System.Net.Http.HttpCompletionOption]::ResponseHeadersRead
} }
else { else {
@ -404,8 +451,7 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
$DownloadException.Data["StatusCode"] = [int] $Response.StatusCode $DownloadException.Data["StatusCode"] = [int] $Response.StatusCode
$DownloadException.Data["ErrorMessage"] = "Unable to download $Uri. Returned HTTP status code: " + $DownloadException.Data["StatusCode"] $DownloadException.Data["ErrorMessage"] = "Unable to download $Uri. Returned HTTP status code: " + $DownloadException.Data["StatusCode"]
if (404 -eq [int] $Response.StatusCode) if (404 -eq [int] $Response.StatusCode) {
{
$cts.Cancel() $cts.Cancel()
} }
} }
@ -444,10 +490,8 @@ function GetHTTPResponse([Uri] $Uri, [bool]$HeaderOnly, [bool]$DisableRedirect,
try { try {
return Invoke-With-Retry $downloadScript $cts.Token return Invoke-With-Retry $downloadScript $cts.Token
} }
finally finally {
{ if ($null -ne $cts) {
if ($null -ne $cts)
{
$cts.Dispose() $cts.Dispose()
} }
} }
@ -565,11 +609,9 @@ function Get-Download-Link([string]$AzureFeed, [string]$SpecificVersion, [string
elseif ($Runtime -eq "windowsdesktop") { elseif ($Runtime -eq "windowsdesktop") {
# The windows desktop runtime is part of the core runtime layout prior to 5.0 # The windows desktop runtime is part of the core runtime layout prior to 5.0
$PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip" $PayloadURL = "$AzureFeed/Runtime/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
if ($SpecificVersion -match '^(\d+)\.(.*)$') if ($SpecificVersion -match '^(\d+)\.(.*)$') {
{
$majorVersion = [int]$Matches[1] $majorVersion = [int]$Matches[1]
if ($majorVersion -ge 5) if ($majorVersion -ge 5) {
{
$PayloadURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip" $PayloadURL = "$AzureFeed/WindowsDesktop/$SpecificVersion/windowsdesktop-runtime-$SpecificProductVersion-win-$CLIArchitecture.zip"
} }
} }
@ -619,8 +661,7 @@ function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion, [stri
if ($productVersionResponse.StatusCode -eq 200) { if ($productVersionResponse.StatusCode -eq 200) {
$productVersion = $productVersionResponse.Content.ReadAsStringAsync().Result.Trim() $productVersion = $productVersionResponse.Content.ReadAsStringAsync().Result.Trim()
if ($productVersion -ne $SpecificVersion) if ($productVersion -ne $SpecificVersion) {
{
Say "Using alternate version $productVersion found in $ProductVersionTxtURL" Say "Using alternate version $productVersion found in $ProductVersionTxtURL"
} }
return $productVersion return $productVersion
@ -635,8 +676,7 @@ function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion, [stri
} }
# Getting the version number with productVersion.txt has failed. Try parsing the download link for a version number. # Getting the version number with productVersion.txt has failed. Try parsing the download link for a version number.
if ([string]::IsNullOrEmpty($PackageDownloadLink)) if ([string]::IsNullOrEmpty($PackageDownloadLink)) {
{
Say-Verbose "Using the default value '$SpecificVersion' as the product version." Say-Verbose "Using the default value '$SpecificVersion' as the product version."
return $SpecificVersion return $SpecificVersion
} }
@ -648,21 +688,21 @@ function Get-Product-Version([string]$AzureFeed, [string]$SpecificVersion, [stri
function Get-Product-Version-Url([string]$AzureFeed, [string]$SpecificVersion, [string]$PackageDownloadLink, [bool]$Flattened) { function Get-Product-Version-Url([string]$AzureFeed, [string]$SpecificVersion, [string]$PackageDownloadLink, [bool]$Flattened) {
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
$majorVersion=$null $majorVersion = $null
if ($SpecificVersion -match '^(\d+)\.(.*)') { if ($SpecificVersion -match '^(\d+)\.(.*)') {
$majorVersion = $Matches[1] -as[int] $majorVersion = $Matches[1] -as [int]
} }
$pvFileName='productVersion.txt' $pvFileName = 'productVersion.txt'
if($Flattened) { if ($Flattened) {
if(-not $Runtime) { if (-not $Runtime) {
$pvFileName='sdk-productVersion.txt' $pvFileName = 'sdk-productVersion.txt'
} }
elseif($Runtime -eq "dotnet") { elseif ($Runtime -eq "dotnet") {
$pvFileName='runtime-productVersion.txt' $pvFileName = 'runtime-productVersion.txt'
} }
else { else {
$pvFileName="$Runtime-productVersion.txt" $pvFileName = "$Runtime-productVersion.txt"
} }
} }
@ -696,16 +736,14 @@ function Get-Product-Version-Url([string]$AzureFeed, [string]$SpecificVersion, [
return $ProductVersionTxtURL return $ProductVersionTxtURL
} }
function Get-ProductVersionFromDownloadLink([string]$PackageDownloadLink, [string]$SpecificVersion) function Get-ProductVersionFromDownloadLink([string]$PackageDownloadLink, [string]$SpecificVersion) {
{
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
#product specific version follows the product name #product specific version follows the product name
#for filename 'dotnet-sdk-3.1.404-win-x64.zip': the product version is 3.1.400 #for filename 'dotnet-sdk-3.1.404-win-x64.zip': the product version is 3.1.400
$filename = $PackageDownloadLink.Substring($PackageDownloadLink.LastIndexOf("/") + 1) $filename = $PackageDownloadLink.Substring($PackageDownloadLink.LastIndexOf("/") + 1)
$filenameParts = $filename.Split('-') $filenameParts = $filename.Split('-')
if ($filenameParts.Length -gt 2) if ($filenameParts.Length -gt 2) {
{
$productVersion = $filenameParts[2] $productVersion = $filenameParts[2]
Say-Verbose "Extracted product version '$productVersion' from download link '$PackageDownloadLink'." Say-Verbose "Extracted product version '$productVersion' from download link '$PackageDownloadLink'."
} }
@ -723,6 +761,9 @@ function Get-User-Share-Path() {
if (!$InstallRoot) { if (!$InstallRoot) {
$InstallRoot = "$env:LocalAppData\Microsoft\dotnet" $InstallRoot = "$env:LocalAppData\Microsoft\dotnet"
} }
elseif ($InstallRoot -like "$env:ProgramFiles\dotnet\?*") {
Say-Warning "The install root specified by the environment variable DOTNET_INSTALL_DIR points to the sub folder of $env:ProgramFiles\dotnet which is the default dotnet install root using .NET SDK installer. It is better to keep aligned with .NET SDK installer."
}
return $InstallRoot return $InstallRoot
} }
@ -735,6 +776,19 @@ function Resolve-Installation-Path([string]$InstallDir) {
return $InstallDir return $InstallDir
} }
function Test-User-Write-Access([string]$InstallDir) {
try {
$tempFileName = [guid]::NewGuid().ToString()
$tempFilePath = Join-Path -Path $InstallDir -ChildPath $tempFileName
New-Item -Path $tempFilePath -ItemType File -Force
Remove-Item $tempFilePath -Force
return $true
}
catch {
return $false
}
}
function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) { function Is-Dotnet-Package-Installed([string]$InstallRoot, [string]$RelativePathToPackage, [string]$SpecificVersion) {
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
@ -810,7 +864,7 @@ function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) {
if (($null -eq $PathWithVersion) -Or ($DirectoriesToUnpack -contains $PathWithVersion)) { if (($null -eq $PathWithVersion) -Or ($DirectoriesToUnpack -contains $PathWithVersion)) {
$DestinationPath = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $entry.FullName) $DestinationPath = Get-Absolute-Path $(Join-Path -Path $OutPath -ChildPath $entry.FullName)
$DestinationDir = Split-Path -Parent $DestinationPath $DestinationDir = Split-Path -Parent $DestinationPath
$OverrideFiles=$OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath)) $OverrideFiles = $OverrideNonVersionedFiles -Or (-Not (Test-Path $DestinationPath))
if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) { if ((-Not $DestinationPath.EndsWith("\")) -And $OverrideFiles) {
New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null New-Item -ItemType Directory -Force -Path $DestinationDir | Out-Null
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles) [System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $DestinationPath, $OverrideNonVersionedFiles)
@ -818,8 +872,7 @@ function Extract-Dotnet-Package([string]$ZipPath, [string]$OutPath) {
} }
} }
} }
catch catch {
{
Say-Error "Failed to extract package. Exception: $_" Say-Error "Failed to extract package. Exception: $_"
throw; throw;
} }
@ -851,6 +904,8 @@ function DownloadFile($Source, [string]$OutPath) {
$File = [System.IO.File]::Create($OutPath) $File = [System.IO.File]::Create($OutPath)
$Stream.CopyTo($File) $Stream.CopyTo($File)
$File.Close() $File.Close()
ValidateRemoteLocalFileSizes -LocalFileOutPath $OutPath -SourceUri $Source
} }
finally { finally {
if ($null -ne $Stream) { if ($null -ne $Stream) {
@ -859,19 +914,40 @@ function DownloadFile($Source, [string]$OutPath) {
} }
} }
function ValidateRemoteLocalFileSizes([string]$LocalFileOutPath, $SourceUri) {
try {
$remoteFileSize = Get-Remote-File-Size -zipUri $SourceUri
$fileSize = [long](Get-Item $LocalFileOutPath).Length
Say "Downloaded file $SourceUri size is $fileSize bytes."
if ((![string]::IsNullOrEmpty($remoteFileSize)) -and !([string]::IsNullOrEmpty($fileSize)) ) {
if ($remoteFileSize -ne $fileSize) {
Say "The remote and local file sizes are not equal. Remote file size is $remoteFileSize bytes and local size is $fileSize bytes. The local package may be corrupted."
}
else {
Say "The remote and local file sizes are equal."
}
}
else {
Say "Either downloaded or local package size can not be measured. One of them may be corrupted."
}
}
catch {
Say "Either downloaded or local package size can not be measured. One of them may be corrupted."
}
}
function SafeRemoveFile($Path) { function SafeRemoveFile($Path) {
try { try {
if (Test-Path $Path) { if (Test-Path $Path) {
Remove-Item $Path Remove-Item $Path
Say-Verbose "The temporary file `"$Path`" was removed." Say-Verbose "The temporary file `"$Path`" was removed."
} }
else else {
{
Say-Verbose "The temporary file `"$Path`" does not exist, therefore is not removed." Say-Verbose "The temporary file `"$Path`" does not exist, therefore is not removed."
} }
} }
catch catch {
{
Say-Warning "Failed to remove the temporary file: `"$Path`", remove it manually." Say-Warning "Failed to remove the temporary file: `"$Path`", remove it manually."
} }
} }
@ -883,7 +959,8 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot) {
if (-Not $env:path.Contains($SuffixedBinPath)) { if (-Not $env:path.Contains($SuffixedBinPath)) {
Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process." Say "Adding to current process PATH: `"$BinPath`". Note: This change will not be visible if PowerShell was run as a child process."
$env:path = $SuffixedBinPath + $env:path $env:path = $SuffixedBinPath + $env:path
} else { }
else {
Say-Verbose "Current process PATH already contains `"$BinPath`"" Say-Verbose "Current process PATH already contains `"$BinPath`""
} }
} }
@ -892,43 +969,67 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot) {
} }
} }
function PrintDryRunOutput($Invocation, $DownloadLinks) function PrintDryRunOutput($Invocation, $DownloadLinks) {
{
Say "Payload URLs:" Say "Payload URLs:"
for ($linkIndex=0; $linkIndex -lt $DownloadLinks.count; $linkIndex++) { for ($linkIndex = 0; $linkIndex -lt $DownloadLinks.count; $linkIndex++) {
Say "URL #$linkIndex - $($DownloadLinks[$linkIndex].type): $($DownloadLinks[$linkIndex].downloadLink)" Say "URL #$linkIndex - $($DownloadLinks[$linkIndex].type): $($DownloadLinks[$linkIndex].downloadLink)"
} }
$RepeatableCommand = ".\$ScriptName -Version `"$SpecificVersion`" -InstallDir `"$InstallRoot`" -Architecture `"$CLIArchitecture`"" $RepeatableCommand = ".\$ScriptName -Version `"$SpecificVersion`" -InstallDir `"$InstallRoot`" -Architecture `"$CLIArchitecture`""
if ($Runtime -eq "dotnet") { if ($Runtime -eq "dotnet") {
$RepeatableCommand+=" -Runtime `"dotnet`"" $RepeatableCommand += " -Runtime `"dotnet`""
} }
elseif ($Runtime -eq "aspnetcore") { elseif ($Runtime -eq "aspnetcore") {
$RepeatableCommand+=" -Runtime `"aspnetcore`"" $RepeatableCommand += " -Runtime `"aspnetcore`""
} }
foreach ($key in $Invocation.BoundParameters.Keys) { foreach ($key in $Invocation.BoundParameters.Keys) {
if (-not (@("Architecture","Channel","DryRun","InstallDir","Runtime","SharedRuntime","Version","Quality","FeedCredential") -contains $key)) { if (-not (@("Architecture", "Channel", "DryRun", "InstallDir", "Runtime", "SharedRuntime", "Version", "Quality", "FeedCredential") -contains $key)) {
$RepeatableCommand+=" -$key `"$($Invocation.BoundParameters[$key])`"" $RepeatableCommand += " -$key `"$($Invocation.BoundParameters[$key])`""
} }
} }
if ($Invocation.BoundParameters.Keys -contains "FeedCredential") { if ($Invocation.BoundParameters.Keys -contains "FeedCredential") {
$RepeatableCommand+=" -FeedCredential `"<feedCredential>`"" $RepeatableCommand += " -FeedCredential `"<feedCredential>`""
} }
Say "Repeatable invocation: $RepeatableCommand" Say "Repeatable invocation: $RepeatableCommand"
if ($SpecificVersion -ne $EffectiveVersion) if ($SpecificVersion -ne $EffectiveVersion) {
{
Say "NOTE: Due to finding a version manifest with this runtime, it would actually install with version '$EffectiveVersion'" Say "NOTE: Due to finding a version manifest with this runtime, it would actually install with version '$EffectiveVersion'"
} }
} }
# grab the 'stem' of the redirect and check it against all of our configured feeds,
# if it matches, we can be sure that the redirect is valid and we should use it for
# subsequent processing
function Sanitize-RedirectUrl([string]$url) {
$urlSegments = ([System.Uri]$url).Segments;
$urlStem = $urlSegments[2..($urlSegments.Length - 1)] -join "";
Write-Verbose "Checking configured feeds for the asset at $urlStem"
foreach ($prospectiveFeed in $feeds) {
$trialUrl = "$prospectiveFeed/$urlStem";
Write-Verbose "Checking $trialUrl"
try {
$trialResponse = Invoke-WebRequest -Uri $trialUrl -Method HEAD
if ($trialResponse.StatusCode -eq 200) {
Write-Verbose "Found a match at $trialUrl"
return $trialUrl;
}
else {
Write-Verbose "No match at $trialUrl"
}
}
catch {
Write-Verbose "Failed to check $trialUrl"
}
}
}
function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Internal, [string]$Product, [string]$Architecture) { function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Internal, [string]$Product, [string]$Architecture) {
Say-Invocation $MyInvocation Say-Invocation $MyInvocation
#quality is not supported for LTS or current channel #quality is not supported for LTS or STS channel
if (![string]::IsNullOrEmpty($Quality) -and (@("LTS", "current") -contains $Channel)) { if (![string]::IsNullOrEmpty($Quality) -and (@("LTS", "STS") -contains $Channel)) {
$Quality = "" $Quality = ""
Say-Warning "Specifying quality for current or LTS channel is not supported, the quality will be ignored." Say-Warning "Specifying quality for STS or LTS channel is not supported, the quality will be ignored."
} }
Say-Verbose "Retrieving primary payload URL from aka.ms link for channel: '$Channel', quality: '$Quality' product: '$Product', os: 'win', architecture: '$Architecture'." Say-Verbose "Retrieving primary payload URL from aka.ms link for channel: '$Channel', quality: '$Quality' product: '$Product', os: 'win', architecture: '$Architecture'."
@ -939,19 +1040,18 @@ function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Intern
} }
$akaMsLink += "/$Channel" $akaMsLink += "/$Channel"
if (-not [string]::IsNullOrEmpty($Quality)) { if (-not [string]::IsNullOrEmpty($Quality)) {
$akaMsLink +="/$Quality" $akaMsLink += "/$Quality"
} }
$akaMsLink +="/$Product-win-$Architecture.zip" $akaMsLink += "/$Product-win-$Architecture.zip"
Say-Verbose "Constructed aka.ms link: '$akaMsLink'." Say-Verbose "Constructed aka.ms link: '$akaMsLink'."
$akaMsDownloadLink=$null $akaMsDownloadLink = $null
for ($maxRedirections = 9; $maxRedirections -ge 0; $maxRedirections--) for ($maxRedirections = 9; $maxRedirections -ge 0; $maxRedirections--) {
{
#get HTTP response #get HTTP response
#do not pass credentials as a part of the $akaMsLink and do not apply credentials in the GetHTTPResponse function #do not pass credentials as a part of the $akaMsLink and do not apply credentials in the GetHTTPResponse function
#otherwise the redirect link would have credentials as well #otherwise the redirect link would have credentials as well
#it would result in applying credentials twice to the resulting link and thus breaking it, and in echoing credentials to the output as a part of redirect link #it would result in applying credentials twice to the resulting link and thus breaking it, and in echoing credentials to the output as a part of redirect link
$Response= GetHTTPResponse -Uri $akaMsLink -HeaderOnly $true -DisableRedirect $true -DisableFeedCredential $true $Response = GetHTTPResponse -Uri $akaMsLink -HeaderOnly $true -DisableRedirect $true -DisableFeedCredential $true
Say-Verbose "Received response:`n$Response" Say-Verbose "Received response:`n$Response"
if ([string]::IsNullOrEmpty($Response)) { if ([string]::IsNullOrEmpty($Response)) {
@ -960,8 +1060,7 @@ function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Intern
} }
#if HTTP code is 301 (Moved Permanently), the redirect link exists #if HTTP code is 301 (Moved Permanently), the redirect link exists
if ($Response.StatusCode -eq 301) if ($Response.StatusCode -eq 301) {
{
try { try {
$akaMsDownloadLink = $Response.Headers.GetValues("Location")[0] $akaMsDownloadLink = $Response.Headers.GetValues("Location")[0]
@ -980,9 +1079,13 @@ function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Intern
return $null return $null
} }
} }
elseif ((($Response.StatusCode -lt 300) -or ($Response.StatusCode -ge 400)) -and (-not [string]::IsNullOrEmpty($akaMsDownloadLink))) elseif ((($Response.StatusCode -lt 300) -or ($Response.StatusCode -ge 400)) -and (-not [string]::IsNullOrEmpty($akaMsDownloadLink))) {
{
# Redirections have ended. # Redirections have ended.
$actualRedirectUrl = Sanitize-RedirectUrl $akaMsDownloadLink
if ($null -ne $actualRedirectUrl) {
$akaMsDownloadLink = $actualRedirectUrl
}
return $akaMsDownloadLink return $akaMsDownloadLink
} }
@ -998,7 +1101,7 @@ function Get-AkaMSDownloadLink([string]$Channel, [string]$Quality, [bool]$Intern
function Get-AkaMsLink-And-Version([string] $NormalizedChannel, [string] $NormalizedQuality, [bool] $Internal, [string] $ProductName, [string] $Architecture) { function Get-AkaMsLink-And-Version([string] $NormalizedChannel, [string] $NormalizedQuality, [bool] $Internal, [string] $ProductName, [string] $Architecture) {
$AkaMsDownloadLink = Get-AkaMSDownloadLink -Channel $NormalizedChannel -Quality $NormalizedQuality -Internal $Internal -Product $ProductName -Architecture $Architecture $AkaMsDownloadLink = Get-AkaMSDownloadLink -Channel $NormalizedChannel -Quality $NormalizedQuality -Internal $Internal -Product $ProductName -Architecture $Architecture
if ([string]::IsNullOrEmpty($AkaMsDownloadLink)){ if ([string]::IsNullOrEmpty($AkaMsDownloadLink)) {
if (-not [string]::IsNullOrEmpty($NormalizedQuality)) { if (-not [string]::IsNullOrEmpty($NormalizedQuality)) {
# if quality is specified - exit with error - there is no fallback approach # if quality is specified - exit with error - there is no fallback approach
Say-Error "Failed to locate the latest version in the channel '$NormalizedChannel' with '$NormalizedQuality' quality for '$ProductName', os: 'win', architecture: '$Architecture'." Say-Error "Failed to locate the latest version in the channel '$NormalizedChannel' with '$NormalizedQuality' quality for '$ProductName', os: 'win', architecture: '$Architecture'."
@ -1031,10 +1134,11 @@ function Get-AkaMsLink-And-Version([string] $NormalizedChannel, [string] $Normal
} }
} }
function Get-Feeds-To-Use() function Get-Feeds-To-Use() {
{
$feeds = @( $feeds = @(
"https://dotnetcli.azureedge.net/dotnet", "https://builds.dotnet.microsoft.com/dotnet"
"https://dotnetcli.azureedge.net/dotnet"
"https://ci.dot.net/public"
"https://dotnetbuilds.azureedge.net/public" "https://dotnetbuilds.azureedge.net/public"
) )
@ -1053,6 +1157,8 @@ function Get-Feeds-To-Use()
} }
} }
Write-Verbose "Initialized feeds: $feeds"
return $feeds return $feeds
} }
@ -1082,26 +1188,39 @@ function Resolve-AssetName-And-RelativePath([string] $Runtime) {
} }
function Prepare-Install-Directory { function Prepare-Install-Directory {
$diskSpaceWarning = "Failed to check the disk space. Installation will continue, but it may fail if you do not have enough disk space.";
if ($PSVersionTable.PSVersion.Major -lt 7) {
Say-Verbose $diskSpaceWarning
return
}
New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null New-Item -ItemType Directory -Force -Path $InstallRoot | Out-Null
$installDrive = $((Get-Item $InstallRoot -Force).PSDrive.Name); $installDrive = $((Get-Item $InstallRoot -Force).PSDrive.Name);
$diskInfo = $null $diskInfo = $null
try{ try {
$diskInfo = Get-PSDrive -Name $installDrive $diskInfo = Get-PSDrive -Name $installDrive
} }
catch{ catch {
Say-Warning "Failed to check the disk space. Installation will continue, but it may fail if you do not have enough disk space." Say-Warning $diskSpaceWarning
} }
# The check is relevant for PS version >= 7, the result can be irrelevant for older versions. See https://github.com/PowerShell/PowerShell/issues/12442.
if ( ($null -ne $diskInfo) -and ($diskInfo.Free / 1MB -le 100)) { if ( ($null -ne $diskInfo) -and ($diskInfo.Free / 1MB -le 100)) {
throw "There is not enough disk space on drive ${installDrive}:" throw "There is not enough disk space on drive ${installDrive}:"
} }
} }
Say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" if ($Help) {
Say "- The SDK needs to be installed without user interaction and without admin rights." Get-Help $PSCommandPath -Examples
Say "- The SDK installation doesn't need to persist across multiple CI runs." exit
Say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n" }
Say-Verbose "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:"
Say-Verbose "- The SDK needs to be installed without user interaction and without admin rights."
Say-Verbose "- The SDK installation doesn't need to persist across multiple CI runs."
Say-Verbose "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.`r`n"
if ($SharedRuntime -and (-not $Runtime)) { if ($SharedRuntime -and (-not $Runtime)) {
$Runtime = "dotnet" $Runtime = "dotnet"
@ -1109,16 +1228,22 @@ if ($SharedRuntime -and (-not $Runtime)) {
$OverrideNonVersionedFiles = !$SkipNonVersionedFiles $OverrideNonVersionedFiles = !$SkipNonVersionedFiles
$CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture Measure-Action "Product discovery" {
$NormalizedQuality = Get-NormalizedQuality $Quality $script:CLIArchitecture = Get-CLIArchitecture-From-Architecture $Architecture
Say-Verbose "Normalized quality: '$NormalizedQuality'" $script:NormalizedQuality = Get-NormalizedQuality $Quality
$NormalizedChannel = Get-NormalizedChannel $Channel Say-Verbose "Normalized quality: '$NormalizedQuality'"
Say-Verbose "Normalized channel: '$NormalizedChannel'" $script:NormalizedChannel = Get-NormalizedChannel $Channel
$NormalizedProduct = Get-NormalizedProduct $Runtime Say-Verbose "Normalized channel: '$NormalizedChannel'"
Say-Verbose "Normalized product: '$NormalizedProduct'" $script:NormalizedProduct = Get-NormalizedProduct $Runtime
$FeedCredential = ValidateFeedCredential $FeedCredential Say-Verbose "Normalized product: '$NormalizedProduct'"
$script:FeedCredential = ValidateFeedCredential $FeedCredential
}
$InstallRoot = Resolve-Installation-Path $InstallDir $InstallRoot = Resolve-Installation-Path $InstallDir
if (-not (Test-User-Write-Access $InstallRoot)) {
Say-Error "The current user doesn't have write access to the installation root '$InstallRoot' to install .NET. Please try specifying a different installation directory using the -InstallDir parameter, or ensure the selected directory has the appropriate permissions."
throw
}
Say-Verbose "InstallRoot: $InstallRoot" Say-Verbose "InstallRoot: $InstallRoot"
$ScriptName = $MyInvocation.MyCommand.Name $ScriptName = $MyInvocation.MyCommand.Name
($assetName, $dotnetPackageRelativePath) = Resolve-AssetName-And-RelativePath -Runtime $Runtime ($assetName, $dotnetPackageRelativePath) = Resolve-AssetName-And-RelativePath -Runtime $Runtime
@ -1127,7 +1252,7 @@ $feeds = Get-Feeds-To-Use
$DownloadLinks = @() $DownloadLinks = @()
if ($Version.ToLowerInvariant() -ne "latest" -and -not [string]::IsNullOrEmpty($Quality)) { if ($Version.ToLowerInvariant() -ne "latest" -and -not [string]::IsNullOrEmpty($Quality)) {
throw "Quality and Version options are not allowed to be specified simultaneously. See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script#options for details." throw "Quality and Version options are not allowed to be specified simultaneously. See https:// learn.microsoft.com/dotnet/core/tools/dotnet-install-script#options for details."
} }
# aka.ms links can only be used if the user did not request a specific version via the command line or a global.json file. # aka.ms links can only be used if the user did not request a specific version via the command line or a global.json file.
@ -1135,13 +1260,12 @@ if ([string]::IsNullOrEmpty($JSonFile) -and ($Version -eq "latest")) {
($DownloadLink, $SpecificVersion, $EffectiveVersion) = Get-AkaMsLink-And-Version $NormalizedChannel $NormalizedQuality $Internal $NormalizedProduct $CLIArchitecture ($DownloadLink, $SpecificVersion, $EffectiveVersion) = Get-AkaMsLink-And-Version $NormalizedChannel $NormalizedQuality $Internal $NormalizedProduct $CLIArchitecture
if ($null -ne $DownloadLink) { if ($null -ne $DownloadLink) {
$DownloadLinks += New-Object PSObject -Property @{downloadLink="$DownloadLink";specificVersion="$SpecificVersion";effectiveVersion="$EffectiveVersion";type='aka.ms'} $DownloadLinks += New-Object PSObject -Property @{downloadLink = "$DownloadLink"; specificVersion = "$SpecificVersion"; effectiveVersion = "$EffectiveVersion"; type = 'aka.ms' }
Say-Verbose "Generated aka.ms link $DownloadLink with version $EffectiveVersion" Say-Verbose "Generated aka.ms link $DownloadLink with version $EffectiveVersion"
if (-Not $DryRun) { if (-Not $DryRun) {
Say-Verbose "Checking if the version $EffectiveVersion is already installed" Say-Verbose "Checking if the version $EffectiveVersion is already installed"
if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) {
{
Say "$assetName with version '$EffectiveVersion' is already installed." Say "$assetName with version '$EffectiveVersion' is already installed."
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot
return return
@ -1152,34 +1276,31 @@ if ([string]::IsNullOrEmpty($JSonFile) -and ($Version -eq "latest")) {
# Primary and legacy links cannot be used if a quality was specified. # Primary and legacy links cannot be used if a quality was specified.
# If we already have an aka.ms link, no need to search the blob feeds. # If we already have an aka.ms link, no need to search the blob feeds.
if ([string]::IsNullOrEmpty($NormalizedQuality) -and 0 -eq $DownloadLinks.count) if ([string]::IsNullOrEmpty($NormalizedQuality) -and 0 -eq $DownloadLinks.count) {
{
foreach ($feed in $feeds) { foreach ($feed in $feeds) {
try { try {
$SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $feed -Channel $Channel -Version $Version -JSonFile $JSonFile $SpecificVersion = Get-Specific-Version-From-Version -AzureFeed $feed -Channel $Channel -Version $Version -JSonFile $JSonFile
$DownloadLink, $EffectiveVersion = Get-Download-Link -AzureFeed $feed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture $DownloadLink, $EffectiveVersion = Get-Download-Link -AzureFeed $feed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $feed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture $LegacyDownloadLink = Get-LegacyDownload-Link -AzureFeed $feed -SpecificVersion $SpecificVersion -CLIArchitecture $CLIArchitecture
$DownloadLinks += New-Object PSObject -Property @{downloadLink="$DownloadLink";specificVersion="$SpecificVersion";effectiveVersion="$EffectiveVersion";type='primary'} $DownloadLinks += New-Object PSObject -Property @{downloadLink = "$DownloadLink"; specificVersion = "$SpecificVersion"; effectiveVersion = "$EffectiveVersion"; type = 'primary' }
Say-Verbose "Generated primary link $DownloadLink with version $EffectiveVersion" Say-Verbose "Generated primary link $DownloadLink with version $EffectiveVersion"
if (-not [string]::IsNullOrEmpty($LegacyDownloadLink)) { if (-not [string]::IsNullOrEmpty($LegacyDownloadLink)) {
$DownloadLinks += New-Object PSObject -Property @{downloadLink="$LegacyDownloadLink";specificVersion="$SpecificVersion";effectiveVersion="$EffectiveVersion";type='legacy'} $DownloadLinks += New-Object PSObject -Property @{downloadLink = "$LegacyDownloadLink"; specificVersion = "$SpecificVersion"; effectiveVersion = "$EffectiveVersion"; type = 'legacy' }
Say-Verbose "Generated legacy link $LegacyDownloadLink with version $EffectiveVersion" Say-Verbose "Generated legacy link $LegacyDownloadLink with version $EffectiveVersion"
} }
if (-Not $DryRun) { if (-Not $DryRun) {
Say-Verbose "Checking if the version $EffectiveVersion is already installed" Say-Verbose "Checking if the version $EffectiveVersion is already installed"
if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) {
{
Say "$assetName with version '$EffectiveVersion' is already installed." Say "$assetName with version '$EffectiveVersion' is already installed."
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot
return return
} }
} }
} }
catch catch {
{
Say-Verbose "Failed to acquire download links from feed $feed. Exception: $_" Say-Verbose "Failed to acquire download links from feed $feed. Exception: $_"
} }
} }
@ -1194,21 +1315,19 @@ if ($DryRun) {
return return
} }
Prepare-Install-Directory Measure-Action "Installation directory preparation" { Prepare-Install-Directory }
$ZipPath = [System.IO.Path]::combine([System.IO.Path]::GetTempPath(), [System.IO.Path]::GetRandomFileName())
Say-Verbose "Zip path: $ZipPath" Say-Verbose "Zip path: $ZipPath"
$DownloadSucceeded = $false $DownloadSucceeded = $false
$DownloadedLink = $null $DownloadedLink = $null
$ErrorMessages = @() $ErrorMessages = @()
foreach ($link in $DownloadLinks) foreach ($link in $DownloadLinks) {
{
Say-Verbose "Downloading `"$($link.type)`" link $($link.downloadLink)" Say-Verbose "Downloading `"$($link.type)`" link $($link.downloadLink)"
try { try {
DownloadFile -Source $link.downloadLink -OutPath $ZipPath Measure-Action "Package download" { DownloadFile -Source $link.downloadLink -OutPath $ZipPath }
Say-Verbose "Download succeeded." Say-Verbose "Download succeeded."
$DownloadSucceeded = $true $DownloadSucceeded = $true
$DownloadedLink = $link $DownloadedLink = $link
@ -1224,7 +1343,8 @@ foreach ($link in $DownloadLinks)
if ($PSItem.Exception.Data.Contains("ErrorMessage")) { if ($PSItem.Exception.Data.Contains("ErrorMessage")) {
$ErrorMessage = $PSItem.Exception.Data["ErrorMessage"] $ErrorMessage = $PSItem.Exception.Data["ErrorMessage"]
} else { }
else {
$ErrorMessage = $PSItem.Exception.Message $ErrorMessage = $PSItem.Exception.Message
} }
@ -1245,7 +1365,7 @@ if (-not $DownloadSucceeded) {
} }
Say "Extracting the archive." Say "Extracting the archive."
Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot Measure-Action "Package extraction" { Extract-Dotnet-Package -ZipPath $ZipPath -OutPath $InstallRoot }
# Check if the SDK version is installed; if not, fail the installation. # Check if the SDK version is installed; if not, fail the installation.
$isAssetInstalled = $false $isAssetInstalled = $false
@ -1269,226 +1389,13 @@ if (!$isAssetInstalled) {
throw "`"$assetName`" with version = $($DownloadedLink.effectiveVersion) failed to install with an unknown error." throw "`"$assetName`" with version = $($DownloadedLink.effectiveVersion) failed to install with an unknown error."
} }
SafeRemoveFile -Path $ZipPath if (-not $KeepZip) {
SafeRemoveFile -Path $ZipPath
}
Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot Measure-Action "Setting up shell environment" { Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot }
Say "Note that the script does not resolve dependencies during installation." Say "Note that the script does not ensure your Windows version is supported during the installation."
Say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install/windows#dependencies" Say "To check the list of supported versions, go to https://learn.microsoft.com/dotnet/core/install/windows#supported-versions"
Say "Installed version is $($DownloadedLink.effectiveVersion)" Say "Installed version is $($DownloadedLink.effectiveVersion)"
Say "Installation finished" Say "Installation finished"
# SIG # Begin signature block
# MIInnQYJKoZIhvcNAQcCoIInjjCCJ4oCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBPbD3vCI+sY1o7
# t+9GwL7gEDtWJk/5Ypegl3ITSKy+X6CCDYEwggX/MIID56ADAgECAhMzAAACzI61
# lqa90clOAAAAAALMMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjIwNTEyMjA0NjAxWhcNMjMwNTExMjA0NjAxWjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQCiTbHs68bADvNud97NzcdP0zh0mRr4VpDv68KobjQFybVAuVgiINf9aG2zQtWK
# No6+2X2Ix65KGcBXuZyEi0oBUAAGnIe5O5q/Y0Ij0WwDyMWaVad2Te4r1Eic3HWH
# UfiiNjF0ETHKg3qa7DCyUqwsR9q5SaXuHlYCwM+m59Nl3jKnYnKLLfzhl13wImV9
# DF8N76ANkRyK6BYoc9I6hHF2MCTQYWbQ4fXgzKhgzj4zeabWgfu+ZJCiFLkogvc0
# RVb0x3DtyxMbl/3e45Eu+sn/x6EVwbJZVvtQYcmdGF1yAYht+JnNmWwAxL8MgHMz
# xEcoY1Q1JtstiY3+u3ulGMvhAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUiLhHjTKWzIqVIp+sM2rOHH11rfQw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDcwNTI5MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAeA8D
# sOAHS53MTIHYu8bbXrO6yQtRD6JfyMWeXaLu3Nc8PDnFc1efYq/F3MGx/aiwNbcs
# J2MU7BKNWTP5JQVBA2GNIeR3mScXqnOsv1XqXPvZeISDVWLaBQzceItdIwgo6B13
# vxlkkSYMvB0Dr3Yw7/W9U4Wk5K/RDOnIGvmKqKi3AwyxlV1mpefy729FKaWT7edB
# d3I4+hldMY8sdfDPjWRtJzjMjXZs41OUOwtHccPazjjC7KndzvZHx/0VWL8n0NT/
# 404vftnXKifMZkS4p2sB3oK+6kCcsyWsgS/3eYGw1Fe4MOnin1RhgrW1rHPODJTG
# AUOmW4wc3Q6KKr2zve7sMDZe9tfylonPwhk971rX8qGw6LkrGFv31IJeJSe/aUbG
# dUDPkbrABbVvPElgoj5eP3REqx5jdfkQw7tOdWkhn0jDUh2uQen9Atj3RkJyHuR0
# GUsJVMWFJdkIO/gFwzoOGlHNsmxvpANV86/1qgb1oZXdrURpzJp53MsDaBY/pxOc
# J0Cvg6uWs3kQWgKk5aBzvsX95BzdItHTpVMtVPW4q41XEvbFmUP1n6oL5rdNdrTM
# j/HXMRk1KCksax1Vxo3qv+13cCsZAaQNaIAvt5LvkshZkDZIP//0Hnq7NnWeYR3z
# 4oFiw9N2n3bb9baQWuWPswG0Dq9YT9kb+Cs4qIIwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIZcjCCGW4CAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAsyOtZamvdHJTgAAAAACzDAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgNQFZgkyG
# luNzcU2g8/R/8PaAnIpTnmBnw3/0HJQjl9wwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQABpLusOOxklzXjvllIe1AgDCgkYd0BN4cT3yQ8uULV
# e+OnVgGOLnPcffCSGZ/SQMgJndoRMBSBd0jH5JxSkSuLXJpEWs1nl4QUg93FxYLr
# pMdFepMsN733h5JuZGcTFf7P23IOxYaVEC+mKLbkOxIJaxgDQYSgliSg9X2hwLJ2
# frCUV4b3ZWL0R495LhGpo65B7Ik/OOeHXWcs8d7vOnE/ObPHFv3fn1QTrq+KvbhA
# TWEmL3P9P0Jn7k6gJjrTOxpgcDenr0IE5X63oe7y32LgLlJbr1OjKQUUCPVQ16d9
# bgkhRp0gghdSAbDKjQuEAQ+e3GTeoNWnzxPlQfMLP0droYIW/DCCFvgGCisGAQQB
# gjcDAwExghboMIIW5AYJKoZIhvcNAQcCoIIW1TCCFtECAQMxDzANBglghkgBZQME
# AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIGXZSvVWqDs0wjW8JLHjKJ41lgzXHpdGqPCyYSvk
# gIH0AgZi1XtjQncYEzIwMjIwODAzMTI1NjA1Ljc2OFowBIACAfSggdCkgc0wgcox
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p
# Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg
# RVNOOjEyQkMtRTNBRS03NEVCMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt
# cCBTZXJ2aWNloIIRUzCCBwwwggT0oAMCAQICEzMAAAGhAYVVmblUXYoAAQAAAaEw
# DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN
# MjExMjAyMTkwNTI0WhcNMjMwMjI4MTkwNTI0WjCByjELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg
# T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046MTJCQy1FM0FFLTc0
# RUIxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0G
# CSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDayTxe5WukkrYxxVuHLYW9BEWCD9kk
# jnnHsOKwGddIPbZlLY+l5ovLDNf+BEMQKAZQI3DX91l1yCDuP9X7tOPC48ZRGXA/
# bf9ql0FK5438gIl7cV528XeEOFwc/A+UbIUfW296Omg8Z62xaQv3jrG4U/priArF
# /er1UA1HNuIGUyqjlygiSPwK2NnFApi1JD+Uef5c47kh7pW1Kj7RnchpFeY9MekP
# QRia7cEaUYU4sqCiJVdDJpefLvPT9EdthlQx75ldx+AwZf2a9T7uQRSBh8tpxPdI
# DDkKiWMwjKTrAY09A3I/jidqPuc8PvX+sqxqyZEN2h4GA0Edjmk64nkIukAK18K5
# nALDLO9SMTxpAwQIHRDtZeTClvAPCEoy1vtPD7f+eqHqStuu+XCkfRjXEpX9+h9f
# rsB0/BgD5CBf3ELLAa8TefMfHZWEJRTPNrbXMKizSrUSkVv/3HP/ZsJpwaz5My2R
# byc3Ah9bT76eBJkyfT5FN9v/KQ0HnxhRMs6HHhTmNx+LztYci+vHf0D3QH1eCjZW
# ZRjp1mOyxpPU2mDMG6gelvJse1JzRADo7YIok/J3Ccbm8MbBbm85iogFltFHecHF
# EFwrsDGBFnNYHMhcbarQNA+gY2e2l9fAkX3MjI7Uklkoz74/P6KIqe5jcd9FPCbb
# SbYH9OLsteeYOQIDAQABo4IBNjCCATIwHQYDVR0OBBYEFBa/IDLbY475VQyKiZSw
# 47l0/cypMB8GA1UdIwQYMBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRY
# MFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01p
# Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEF
# BQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w
# a2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo
# MSkuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI
# hvcNAQELBQADggIBACDDIxElfXlG5YKcKrLPSS+f3JWZprwKEiASvivaHTBRlXtA
# s+TkadcsEei+9w5vmF5tCUzTH4c0nCI7bZxnsL+S6XsiOs3Z1V4WX+IwoXUJ4zLv
# s0+mT4vjGDtYfKQ/bsmJKar2c99m/fHv1Wm2CTcyaePvi86Jh3UyLjdRILWbtzs4
# oImFMwwKbzHdPopxrBhgi+C1YZshosWLlgzyuxjUl+qNg1m52MJmf11loI7D9HJo
# aQzd+rf928Y8rvULmg2h/G50o+D0UJ1Fa/cJJaHfB3sfKw9X6GrtXYGjmM3+g+Ah
# aVsfupKXNtOFu5tnLKvAH5OIjEDYV1YKmlXuBuhbYassygPFMmNgG2Ank3drEcDc
# ZhCXXqpRszNo1F6Gu5JCpQZXbOJM9Ue5PlJKtmImAYIGsw+pnHy/r5ggSYOp4g5Z
# 1oU9GhVCM3V0T9adee6OUXBk1rE4dZc/UsPlj0qoiljL+lN1A5gkmmz7k5tIObVG
# B7dJdz8J0FwXRE5qYu1AdvauVbZwGQkL1x8aK/svjEQW0NUyJ29znDHiXl5vLoRT
# jjFpshUBi2+IY+mNqbLmj24j5eT+bjDlE3HmNtLPpLcMDYqZ1H+6U6YmaiNmac2j
# RXDAaeEE/uoDMt2dArfJP7M+MDv3zzNNTINeuNEtDVgm9zwfgIUCXnDZuVtiMIIH
# cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB
# iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
# ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp
# TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw
# OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ
# Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh
# C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx
# WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc
# UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc
# nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo
# veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi
# YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9
# fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH
# GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X
# KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE
# R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/
# eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3
# FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd
# BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE
# AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI
# MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB
# Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud
# HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By
# b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO
# MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy
# dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC
# AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk
# bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng
# ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3
# lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC
# gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6
# MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU
# BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh
# VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+
# fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp
# NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI
# qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCAsowggIzAgEBMIH4
# oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G
# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw
# IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1U
# aGFsZXMgVFNTIEVTTjoxMkJDLUUzQUUtNzRFQjElMCMGA1UEAxMcTWljcm9zb2Z0
# IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIaAxUAG3F2jO4LEMVLwgKG
# XdYMN4FBgOCggYMwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAN
# BgkqhkiG9w0BAQUFAAIFAOaUaAIwIhgPMjAyMjA4MDMxMTIwMzRaGA8yMDIyMDgw
# NDExMjAzNFowczA5BgorBgEEAYRZCgQBMSswKTAKAgUA5pRoAgIBADAGAgEAAgEI
# MAcCAQACAhIDMAoCBQDmlbmCAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQB
# hFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEA
# bqvBFqohycksQZhSEJZTiCeQ6hwWlYWRXL1PerCFbLmK+4vgr57BkwFsu5KzBE2z
# i1eHNrssK4BcBLYyIhDIjMSqqtrvclrB6SSDag1WcxZrz42xatvyhKXZd52a5R5Q
# xJw66cvwkDa4UmEtVOnbkaOPyyAql72D9w/XLHY0nmUxggQNMIIECQIBATCBkzB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAaEBhVWZuVRdigABAAAB
# oTANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEE
# MC8GCSqGSIb3DQEJBDEiBCCGB/0CqUv9yvdxWNnaciRCHPCM4WmcYpKBUNiR1Xg+
# yjCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIOsIVPE6gYJoIIKOhHIF7UlJ
# Cswl4IJPISvOKInfjtCEMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAGhAYVVmblUXYoAAQAAAaEwIgQgy8rtxrFwV1rcLxTDP8W6Af+y
# Za2AaaMVTQY3E9A2eHMwDQYJKoZIhvcNAQELBQAEggIAEJIvjnhqXrrsyJWHHG9i
# gKBBM3d51KglP0nJ0dY1zp7uUIBTQ53LVONE1SFiqbw6akydYum6iTaI7tvFWJRW
# dx5Fq56gt+QY2YO0nsn3zH3ulyUUkhHuMsx5N/pQT6tsEMu6tWCuWucf44JQHlyY
# x8/C+S5QoA7DId3ugccCFpZWUMb76QWReDtalDz3XY/gNSBT2DTJ8WT78WREcYKu
# aBO52cUXKKLtr5ZoPcdEElB/TPuctcC6Hh0+J4Y6PCNwOVPodpEmjMSV0tAN8tZp
# T3cyf9YPnwXNdDiaikZlPSO0pXCM5+KjrBm5hnj6+J8qMc+Qc91UMh2J96kZWVmm
# PsWE7YA4DlWrIWn2mdGLtTJP4sOlqRjigP9rdBFo0oG9c1ySKw3rN7zpGTDnFdkS
# vxeCoLWx48BJ4bE3Siwx6cwrYScmIgyobLb1Ztu5FEmFUn8maX5oo8IY9kPsODOG
# 3y8hPoLOOj2lRdslV9bdjtrbnqeY5Nq/oKuftbX8iD2MYFgWOqeufw3TcQiSz2uF
# tKGolAePdRf1S7c81CC89g7tcwy1TILR9M2JdWOwosAtpFXxX6Vc1OGiRwPAyXBL
# dvDqiTx5zb9k87hfJvwix/oXfo4fNCNdE/i/VbmsAJjcxd+eEBbJ9Oc+oPqC/5zq
# pPtLXUsVfUWX58dPRnYeMAg=
# SIG # End signature block

View File

@ -298,11 +298,20 @@ get_machine_architecture() {
if command -v uname > /dev/null; then if command -v uname > /dev/null; then
CPUName=$(uname -m) CPUName=$(uname -m)
case $CPUName in case $CPUName in
armv1*|armv2*|armv3*|armv4*|armv5*|armv6*)
echo "armv6-or-below"
return 0
;;
armv*l) armv*l)
echo "arm" echo "arm"
return 0 return 0
;; ;;
aarch64|arm64) aarch64|arm64)
if [ "$(getconf LONG_BIT)" -lt 64 ]; then
# This is 32-bit OS running on 64-bit CPU (for example Raspberry Pi OS)
echo "arm"
return 0
fi
echo "arm64" echo "arm64"
return 0 return 0
;; ;;
@ -310,6 +319,22 @@ get_machine_architecture() {
echo "s390x" echo "s390x"
return 0 return 0
;; ;;
ppc64le)
echo "ppc64le"
return 0
;;
loongarch64)
echo "loongarch64"
return 0
;;
riscv64)
echo "riscv64"
return 0
;;
powerpc|ppc)
echo "ppc"
return 0
;;
esac esac
fi fi
@ -326,7 +351,13 @@ get_normalized_architecture_from_architecture() {
local architecture="$(to_lowercase "$1")" local architecture="$(to_lowercase "$1")"
if [[ $architecture == \<auto\> ]]; then if [[ $architecture == \<auto\> ]]; then
echo "$(get_machine_architecture)" machine_architecture="$(get_machine_architecture)"
if [[ "$machine_architecture" == "armv6-or-below" ]]; then
say_err "Architecture \`$machine_architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues"
return 1
fi
echo $machine_architecture
return 0 return 0
fi fi
@ -347,6 +378,14 @@ get_normalized_architecture_from_architecture() {
echo "s390x" echo "s390x"
return 0 return 0
;; ;;
ppc64le)
echo "ppc64le"
return 0
;;
loongarch64)
echo "loongarch64"
return 0
;;
esac esac
say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues" say_err "Architecture \`$architecture\` not supported. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues"
@ -384,11 +423,17 @@ get_normalized_architecture_for_specific_sdk_version() {
# args: # args:
# version or channel - $1 # version or channel - $1
is_arm64_supported() { is_arm64_supported() {
#any channel or version that starts with the specified versions # Extract the major version by splitting on the dot
case "$1" in major_version="${1%%.*}"
( "1"* | "2"* | "3"* | "4"* | "5"*)
# Check if the major version is a valid number and less than 6
case "$major_version" in
[0-9]*)
if [ "$major_version" -lt 6 ]; then
echo false echo false
return 0 return 0
fi
;;
esac esac
echo true echo true
@ -407,8 +452,13 @@ get_normalized_os() {
echo "$osname" echo "$osname"
return 0 return 0
;; ;;
macos)
osname='osx'
echo "$osname"
return 0
;;
*) *)
say_err "'$user_defined_os' is not a supported value for --os option, supported values are: osx, linux, linux-musl, freebsd, rhel.6. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues." say_err "'$user_defined_os' is not a supported value for --os option, supported values are: osx, macos, linux, linux-musl, freebsd, rhel.6. If you think this is a bug, report it at https://github.com/dotnet/install-scripts/issues."
return 1 return 1
;; ;;
esac esac
@ -451,6 +501,10 @@ get_normalized_channel() {
local channel="$(to_lowercase "$1")" local channel="$(to_lowercase "$1")"
if [[ $channel == current ]]; then
say_warning 'Value "Current" is deprecated for -Channel option. Use "STS" instead.'
fi
if [[ $channel == release/* ]]; then if [[ $channel == release/* ]]; then
say_warning 'Using branch name with -Channel option is no longer supported with newer releases. Use -Quality option with a channel in X.Y format instead.'; say_warning 'Using branch name with -Channel option is no longer supported with newer releases. Use -Quality option with a channel in X.Y format instead.';
fi fi
@ -461,6 +515,14 @@ get_normalized_channel() {
echo "LTS" echo "LTS"
return 0 return 0
;; ;;
sts)
echo "STS"
return 0
;;
current)
echo "STS"
return 0
;;
*) *)
echo "$channel" echo "$channel"
return 0 return 0
@ -526,6 +588,40 @@ is_dotnet_package_installed() {
fi fi
} }
# args:
# downloaded file - $1
# remote_file_size - $2
validate_remote_local_file_sizes()
{
eval $invocation
local downloaded_file="$1"
local remote_file_size="$2"
local file_size=''
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
file_size="$(stat -c '%s' "$downloaded_file")"
elif [[ "$OSTYPE" == "darwin"* ]]; then
# hardcode in order to avoid conflicts with GNU stat
file_size="$(/usr/bin/stat -f '%z' "$downloaded_file")"
fi
if [ -n "$file_size" ]; then
say "Downloaded file size is $file_size bytes."
if [ -n "$remote_file_size" ] && [ -n "$file_size" ]; then
if [ "$remote_file_size" -ne "$file_size" ]; then
say "The remote and local file sizes are not equal. The remote file size is $remote_file_size bytes and the local size is $file_size bytes. The local package may be corrupted."
else
say "The remote and local file sizes are equal."
fi
fi
else
say "Either downloaded or local package size can not be measured. One of them may be corrupted."
fi
}
# args: # args:
# azure_feed - $1 # azure_feed - $1
# channel - $2 # channel - $2
@ -860,6 +956,37 @@ get_absolute_path() {
return 0 return 0
} }
# args:
# override - $1 (boolean, true or false)
get_cp_options() {
eval $invocation
local override="$1"
local override_switch=""
if [ "$override" = false ]; then
override_switch="-n"
# create temporary files to check if 'cp -u' is supported
tmp_dir="$(mktemp -d)"
tmp_file="$tmp_dir/testfile"
tmp_file2="$tmp_dir/testfile2"
touch "$tmp_file"
# use -u instead of -n if it's available
if cp -u "$tmp_file" "$tmp_file2" 2>/dev/null; then
override_switch="-u"
fi
# clean up
rm -f "$tmp_file" "$tmp_file2"
rm -rf "$tmp_dir"
fi
echo "$override_switch"
}
# args: # args:
# input_files - stdin # input_files - stdin
# root_path - $1 # root_path - $1
@ -871,15 +998,7 @@ copy_files_or_dirs_from_list() {
local root_path="$(remove_trailing_slash "$1")" local root_path="$(remove_trailing_slash "$1")"
local out_path="$(remove_trailing_slash "$2")" local out_path="$(remove_trailing_slash "$2")"
local override="$3" local override="$3"
local osname="$(get_current_os_name)" local override_switch="$(get_cp_options "$override")"
local override_switch=$(
if [ "$override" = false ]; then
if [ "$osname" = "linux-musl" ]; then
printf -- "-u";
else
printf -- "-n";
fi
fi)
cat | uniq | while read -r file_path; do cat | uniq | while read -r file_path; do
local path="$(remove_beginning_slash "${file_path#$root_path}")" local path="$(remove_beginning_slash "${file_path#$root_path}")"
@ -894,14 +1013,39 @@ copy_files_or_dirs_from_list() {
done done
} }
# args:
# zip_uri - $1
get_remote_file_size() {
local zip_uri="$1"
if machine_has "curl"; then
file_size=$(curl -sI "$zip_uri" | grep -i content-length | awk '{ num = $2 + 0; print num }')
elif machine_has "wget"; then
file_size=$(wget --spider --server-response -O /dev/null "$zip_uri" 2>&1 | grep -i 'Content-Length:' | awk '{ num = $2 + 0; print num }')
else
say "Neither curl nor wget is available on this system."
return
fi
if [ -n "$file_size" ]; then
say "Remote file $zip_uri size is $file_size bytes."
echo "$file_size"
else
say_verbose "Content-Length header was not extracted for $zip_uri."
echo ""
fi
}
# args: # args:
# zip_path - $1 # zip_path - $1
# out_path - $2 # out_path - $2
# remote_file_size - $3
extract_dotnet_package() { extract_dotnet_package() {
eval $invocation eval $invocation
local zip_path="$1" local zip_path="$1"
local out_path="$2" local out_path="$2"
local remote_file_size="$3"
local temp_out_path="$(mktemp -d "$temporary_file_template")" local temp_out_path="$(mktemp -d "$temporary_file_template")"
@ -912,8 +1056,12 @@ extract_dotnet_package() {
find "$temp_out_path" -type f | grep -Eo "$folders_with_version_regex" | sort | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" false find "$temp_out_path" -type f | grep -Eo "$folders_with_version_regex" | sort | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" false
find "$temp_out_path" -type f | grep -Ev "$folders_with_version_regex" | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" "$override_non_versioned_files" find "$temp_out_path" -type f | grep -Ev "$folders_with_version_regex" | copy_files_or_dirs_from_list "$temp_out_path" "$out_path" "$override_non_versioned_files"
validate_remote_local_file_sizes "$zip_path" "$remote_file_size"
rm -rf "$temp_out_path" rm -rf "$temp_out_path"
rm -f "$zip_path" && say_verbose "Temporary zip file $zip_path was removed" if [ -z ${keep_zip+x} ]; then
rm -f "$zip_path" && say_verbose "Temporary archive file $zip_path was removed"
fi
if [ "$failed" = true ]; then if [ "$failed" = true ]; then
say_err "Extraction failed" say_err "Extraction failed"
@ -1124,13 +1272,69 @@ downloadwget() {
return 0 return 0
} }
extract_stem() {
local url="$1"
# extract the protocol
proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"
# remove the protocol
url="${1/$proto/}"
# extract the path (if any) - since we know all of our feeds have a first path segment, we can skip the first one. otherwise we'd use -f2- to get the full path
full_path="$(echo $url | grep / | cut -d/ -f2-)"
path="$(echo $full_path | cut -d/ -f2-)"
echo $path
}
check_url_exists() {
eval $invocation
local url="$1"
local code=""
if machine_has "curl"
then
code=$(curl --head -o /dev/null -w "%{http_code}" -s --fail "$url");
elif machine_has "wget"
then
# get the http response, grab the status code
server_response=$(wget -qO- --method=HEAD --server-response "$url" 2>&1)
code=$(echo "$server_response" | grep "HTTP/" | awk '{print $2}')
fi
if [ $code = "200" ]; then
return 0
else
return 1
fi
}
sanitize_redirect_url() {
eval $invocation
local url_stem
url_stem=$(extract_stem "$1")
say_verbose "Checking configured feeds for the asset at ${yellow:-}$url_stem${normal:-}"
for feed in "${feeds[@]}"
do
local trial_url="$feed/$url_stem"
say_verbose "Checking ${yellow:-}$trial_url${normal:-}"
if check_url_exists "$trial_url"; then
say_verbose "Found a match at ${yellow:-}$trial_url${normal:-}"
echo "$trial_url"
return 0
else
say_verbose "No match at ${yellow:-}$trial_url${normal:-}"
fi
done
return 1
}
get_download_link_from_aka_ms() { get_download_link_from_aka_ms() {
eval $invocation eval $invocation
#quality is not supported for LTS or current channel #quality is not supported for LTS or STS channel
if [[ ! -z "$normalized_quality" && ("$normalized_channel" == "LTS" || "$normalized_channel" == "current") ]]; then #STS maps to current
if [[ ! -z "$normalized_quality" && ("$normalized_channel" == "LTS" || "$normalized_channel" == "STS") ]]; then
normalized_quality="" normalized_quality=""
say_warning "Specifying quality for current or LTS channel is not supported, the quality will be ignored." say_warning "Specifying quality for STS or LTS channel is not supported, the quality will be ignored."
fi fi
say_verbose "Retrieving primary payload URL from aka.ms for channel: '$normalized_channel', quality: '$normalized_quality', product: '$normalized_product', os: '$normalized_os', architecture: '$normalized_architecture'." say_verbose "Retrieving primary payload URL from aka.ms for channel: '$normalized_channel', quality: '$normalized_quality', product: '$normalized_product', os: '$normalized_os', architecture: '$normalized_architecture'."
@ -1159,6 +1363,12 @@ get_download_link_from_aka_ms() {
http_codes=$( echo "$response" | awk '$1 ~ /^HTTP/ {print $2}' ) http_codes=$( echo "$response" | awk '$1 ~ /^HTTP/ {print $2}' )
# They all need to be 301, otherwise some links are broken (except for the last, which is not a redirect but 200 or 404). # They all need to be 301, otherwise some links are broken (except for the last, which is not a redirect but 200 or 404).
broken_redirects=$( echo "$http_codes" | sed '$d' | grep -v '301' ) broken_redirects=$( echo "$http_codes" | sed '$d' | grep -v '301' )
# The response may end without final code 2xx/4xx/5xx somehow, e.g. network restrictions on www.bing.com causes redirecting to bing.com fails with connection refused.
# In this case it should not exclude the last.
last_http_code=$( echo "$http_codes" | tail -n 1 )
if ! [[ $last_http_code =~ ^(2|4|5)[0-9][0-9]$ ]]; then
broken_redirects=$( echo "$http_codes" | grep -v '301' )
fi
# All HTTP codes are 301 (Moved Permanently), the redirect link exists. # All HTTP codes are 301 (Moved Permanently), the redirect link exists.
if [[ -z "$broken_redirects" ]]; then if [[ -z "$broken_redirects" ]]; then
@ -1169,6 +1379,11 @@ get_download_link_from_aka_ms() {
return 1 return 1
fi fi
sanitized_redirect_url=$(sanitize_redirect_url "$aka_ms_download_link")
if [[ -n "$sanitized_redirect_url" ]]; then
aka_ms_download_link="$sanitized_redirect_url"
fi
say_verbose "The redirect location retrieved: '$aka_ms_download_link'." say_verbose "The redirect location retrieved: '$aka_ms_download_link'."
return 0 return 0
else else
@ -1180,7 +1395,9 @@ get_download_link_from_aka_ms() {
get_feeds_to_use() get_feeds_to_use()
{ {
feeds=( feeds=(
"https://builds.dotnet.microsoft.com/dotnet"
"https://dotnetcli.azureedge.net/dotnet" "https://dotnetcli.azureedge.net/dotnet"
"https://ci.dot.net/public"
"https://dotnetbuilds.azureedge.net/public" "https://dotnetbuilds.azureedge.net/public"
) )
@ -1239,7 +1456,7 @@ generate_akams_links() {
normalized_version="$(to_lowercase "$version")" normalized_version="$(to_lowercase "$version")"
if [[ "$normalized_version" != "latest" ]] && [ -n "$normalized_quality" ]; then if [[ "$normalized_version" != "latest" ]] && [ -n "$normalized_quality" ]; then
say_err "Quality and Version options are not allowed to be specified simultaneously. See https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-install-script#options for details." say_err "Quality and Version options are not allowed to be specified simultaneously. See https://learn.microsoft.com/dotnet/core/tools/dotnet-install-script#options for details."
return 1 return 1
fi fi
@ -1406,10 +1623,11 @@ install_dotnet() {
eval $invocation eval $invocation
local download_failed=false local download_failed=false
local download_completed=false local download_completed=false
local remote_file_size=0
mkdir -p "$install_root" mkdir -p "$install_root"
zip_path="$(mktemp "$temporary_file_template")" zip_path="${zip_path:-$(mktemp "$temporary_file_template")}"
say_verbose "Zip path: $zip_path" say_verbose "Archive path: $zip_path"
for link_index in "${!download_links[@]}" for link_index in "${!download_links[@]}"
do do
@ -1433,7 +1651,7 @@ install_dotnet() {
say "Failed to download $link_type link '$download_link': $download_error_msg" say "Failed to download $link_type link '$download_link': $download_error_msg"
;; ;;
esac esac
rm -f "$zip_path" 2>&1 && say_verbose "Temporary zip file $zip_path was removed" rm -f "$zip_path" 2>&1 && say_verbose "Temporary archive file $zip_path was removed"
else else
download_completed=true download_completed=true
break break
@ -1446,8 +1664,10 @@ install_dotnet() {
return 1 return 1
fi fi
say "Extracting zip from $download_link" remote_file_size="$(get_remote_file_size "$download_link")"
extract_dotnet_package "$zip_path" "$install_root" || return 1
say "Extracting archive from $download_link"
extract_dotnet_package "$zip_path" "$install_root" "$remote_file_size" || return 1
# Check if the SDK version is installed; if not, fail the installation. # Check if the SDK version is installed; if not, fail the installation.
# if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed. # if the version contains "RTM" or "servicing"; check if a 'release-type' SDK version is installed.
@ -1597,25 +1817,42 @@ do
override_non_versioned_files=false override_non_versioned_files=false
non_dynamic_parameters+=" $name" non_dynamic_parameters+=" $name"
;; ;;
--keep-zip|-[Kk]eep[Zz]ip)
keep_zip=true
non_dynamic_parameters+=" $name"
;;
--zip-path|-[Zz]ip[Pp]ath)
shift
zip_path="$1"
;;
-?|--?|-h|--help|-[Hh]elp) -?|--?|-h|--help|-[Hh]elp)
script_name="$(basename "$0")" script_name="dotnet-install.sh"
echo ".NET Tools Installer" echo ".NET Tools Installer"
echo "Usage: $script_name [-c|--channel <CHANNEL>] [-v|--version <VERSION>] [-p|--prefix <DESTINATION>]" echo "Usage:"
echo " # Install a .NET SDK of a given Quality from a given Channel"
echo " $script_name [-c|--channel <CHANNEL>] [-q|--quality <QUALITY>]"
echo " # Install a .NET SDK of a specific public version"
echo " $script_name [-v|--version <VERSION>]"
echo " $script_name -h|-?|--help" echo " $script_name -h|-?|--help"
echo "" echo ""
echo "$script_name is a simple command line interface for obtaining dotnet cli." echo "$script_name is a simple command line interface for obtaining dotnet cli."
echo " Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:"
echo " - The SDK needs to be installed without user interaction and without admin rights."
echo " - The SDK installation doesn't need to persist across multiple CI runs."
echo " To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer."
echo "" echo ""
echo "Options:" echo "Options:"
echo " -c,--channel <CHANNEL> Download from the channel specified, Defaults to \`$channel\`." echo " -c,--channel <CHANNEL> Download from the channel specified, Defaults to \`$channel\`."
echo " -Channel" echo " -Channel"
echo " Possible values:" echo " Possible values:"
echo " - Current - most current release" echo " - STS - the most recent Standard Term Support release"
echo " - LTS - most current supported release" echo " - LTS - the most recent Long Term Support release"
echo " - 2-part version in a format A.B - represents a specific release" echo " - 2-part version in a format A.B - represents a specific release"
echo " examples: 2.0; 1.0" echo " examples: 2.0; 1.0"
echo " - 3-part version in a format A.B.Cxx - represents a specific SDK release" echo " - 3-part version in a format A.B.Cxx - represents a specific SDK release"
echo " examples: 5.0.1xx, 5.0.2xx." echo " examples: 5.0.1xx, 5.0.2xx."
echo " Supported since 5.0 release" echo " Supported since 5.0 release"
echo " Warning: Value 'Current' is deprecated for the Channel parameter. Use 'STS' instead."
echo " Note: The version parameter overrides the channel parameter when any version other than 'latest' is used." echo " Note: The version parameter overrides the channel parameter when any version other than 'latest' is used."
echo " -v,--version <VERSION> Use specific VERSION, Defaults to \`$version\`." echo " -v,--version <VERSION> Use specific VERSION, Defaults to \`$version\`."
echo " -Version" echo " -Version"
@ -1626,7 +1863,7 @@ do
echo " -q,--quality <quality> Download the latest build of specified quality in the channel." echo " -q,--quality <quality> Download the latest build of specified quality in the channel."
echo " -Quality" echo " -Quality"
echo " The possible values are: daily, signed, validated, preview, GA." echo " The possible values are: daily, signed, validated, preview, GA."
echo " Works only in combination with channel. Not applicable for current and LTS channels and will be ignored if those channels are used." echo " Works only in combination with channel. Not applicable for STS and LTS channels and will be ignored if those channels are used."
echo " For SDK use channel in A.B.Cxx format. Using quality for SDK together with channel in A.B format is not supported." echo " For SDK use channel in A.B.Cxx format. Using quality for SDK together with channel in A.B format is not supported."
echo " Supported since 5.0 release." echo " Supported since 5.0 release."
echo " Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality." echo " Note: The version parameter overrides the channel parameter when any version other than 'latest' is used, and therefore overrides the quality."
@ -1637,7 +1874,7 @@ do
echo " -InstallDir" echo " -InstallDir"
echo " --architecture <ARCHITECTURE> Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`." echo " --architecture <ARCHITECTURE> Architecture of dotnet binaries to be installed, Defaults to \`$architecture\`."
echo " --arch,-Architecture,-Arch" echo " --arch,-Architecture,-Arch"
echo " Possible values: x64, arm, arm64 and s390x" echo " Possible values: x64, arm, arm64, s390x, ppc64le and loongarch64"
echo " --os <system> Specifies operating system to be used when selecting the installer." echo " --os <system> Specifies operating system to be used when selecting the installer."
echo " Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6." echo " Overrides the OS determination approach used by the script. Supported values: osx, linux, linux-musl, freebsd, rhel.6."
echo " In case any other value is provided, the platform will be determined by the script based on machine configuration." echo " In case any other value is provided, the platform will be determined by the script based on machine configuration."
@ -1662,6 +1899,8 @@ do
echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly." echo " --no-cdn,-NoCdn Disable downloading from the Azure CDN, and use the uncached feed directly."
echo " --jsonfile <JSONFILE> Determines the SDK version from a user specified global.json file." echo " --jsonfile <JSONFILE> Determines the SDK version from a user specified global.json file."
echo " Note: global.json must have a value for 'SDK:Version'" echo " Note: global.json must have a value for 'SDK:Version'"
echo " --keep-zip,-KeepZip If set, downloaded file is kept."
echo " --zip-path, -ZipPath If set, downloaded file is stored at the specified path."
echo " -?,--?,-h,--help,-Help Shows this help message" echo " -?,--?,-h,--help,-Help Shows this help message"
echo "" echo ""
echo "Install Location:" echo "Install Location:"
@ -1680,10 +1919,10 @@ do
shift shift
done done
say "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:" say_verbose "Note that the intended use of this script is for Continuous Integration (CI) scenarios, where:"
say "- The SDK needs to be installed without user interaction and without admin rights." say_verbose "- The SDK needs to be installed without user interaction and without admin rights."
say "- The SDK installation doesn't need to persist across multiple CI runs." say_verbose "- The SDK installation doesn't need to persist across multiple CI runs."
say "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.\n" say_verbose "To set up a development environment or to run apps, use installers rather than this script. Visit https://dotnet.microsoft.com/download to get the installer.\n"
if [ "$internal" = true ] && [ -z "$(echo $feed_credential)" ]; then if [ "$internal" = true ] && [ -z "$(echo $feed_credential)" ]; then
message="Provide credentials via --feed-credential parameter." message="Provide credentials via --feed-credential parameter."
@ -1716,5 +1955,5 @@ else
fi fi
say "Note that the script does not resolve dependencies during installation." say "Note that the script does not resolve dependencies during installation."
say "To check the list of dependencies, go to https://docs.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section." say "To check the list of dependencies, go to https://learn.microsoft.com/dotnet/core/install, select your operating system and check the \"Dependencies\" section."
say "Installation finished successfully." say "Installation finished successfully."