diff --git a/src/dotnet-install.ps1 b/src/dotnet-install.ps1 index 913cbcb3c9..1a4cf8c622 100644 --- a/src/dotnet-install.ps1 +++ b/src/dotnet-install.ps1 @@ -869,12 +869,12 @@ function Prepend-Sdk-InstallRoot-To-Path([string]$InstallRoot) { } } -function PrintDryRunOutput($Invocation, [string] $DownloadLink, [string] $LegacyDownloadLink, [string] $SpecificVersion, [string] $EffectiveVersion) +function PrintDryRunOutput($Invocation, $DownloadLinks) { Say "Payload URLs:" - Say "Primary named payload URL: ${DownloadLink}" - if ($LegacyDownloadLink) { - Say "Legacy named payload URL: ${LegacyDownloadLink}" + + for ($linkIndex=0; $linkIndex -lt $DownloadLinks.count; $linkIndex++) { + Say "URL #$linkIndex - $($DownloadLinks[$linkIndex].type): $($DownloadLinks[$linkIndex].downloadLink)" } $RepeatableCommand = ".\$ScriptName -Version `"$SpecificVersion`" -InstallDir `"$InstallRoot`" -Architecture `"$CLIArchitecture`"" if ($Runtime -eq "dotnet") { @@ -1111,17 +1111,14 @@ if ([string]::IsNullOrEmpty($JSonFile) -and ($Version -eq "latest")) { $DownloadLinks += New-Object PSObject -Property @{downloadLink="$DownloadLink";specificVersion="$SpecificVersion";effectiveVersion="$EffectiveVersion";type='aka.ms'} Say-Verbose "Generated aka.ms link $DownloadLink with version $EffectiveVersion" - if ($DryRun) { - PrintDryRunOutput $MyInvocation $DownloadLink $null $SpecificVersion $EffectiveVersion - return - } - - Say-Verbose "Checking if the version $EffectiveVersion is already installed" - if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) - { - Say "$assetName version $EffectiveVersion is already installed." - Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot - return + if (-Not $DryRun) { + Say-Verbose "Checking if the version $EffectiveVersion is already installed" + if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) + { + Say "$assetName with version '$EffectiveVersion' is already installed." + Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot + return + } } } } @@ -1141,20 +1138,17 @@ if ([string]::IsNullOrEmpty($NormalizedQuality) -and 0 -eq $DownloadLinks.count) if (-not [string]::IsNullOrEmpty($LegacyDownloadLink)) { $DownloadLinks += New-Object PSObject -Property @{downloadLink="$LegacyDownloadLink";specificVersion="$SpecificVersion";effectiveVersion="$EffectiveVersion";type='legacy'} - Say-Verbose "Generated legacy link $DownloadLink with version $EffectiveVersion" + Say-Verbose "Generated legacy link $LegacyDownloadLink with version $EffectiveVersion" } - if ($DryRun) { - PrintDryRunOutput $MyInvocation $DownloadLink $LegacyDownloadLink $SpecificVersion $EffectiveVersion - return - } - - Say-Verbose "Checking if the version $EffectiveVersion is already installed" - if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) - { - Say "$assetName version $EffeciveVersion is already installed." - Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot - return + if (-Not $DryRun) { + Say-Verbose "Checking if the version $EffectiveVersion is already installed" + if (Is-Dotnet-Package-Installed -InstallRoot $InstallRoot -RelativePathToPackage $dotnetPackageRelativePath -SpecificVersion $EffectiveVersion) + { + Say "$assetName with version '$EffectiveVersion' is already installed." + Prepend-Sdk-InstallRoot-To-Path -InstallRoot $InstallRoot + return + } } } catch @@ -1168,6 +1162,10 @@ if ($DownloadLinks.count -eq 0) { throw "Failed to resolve the exact version number." } +if ($DryRun) { + PrintDryRunOutput $MyInvocation $DownloadLinks + return +} Prepare-Install-Directory diff --git a/src/dotnet-install.sh b/src/dotnet-install.sh index b80ff63934..62e14c139b 100755 --- a/src/dotnet-install.sh +++ b/src/dotnet-install.sh @@ -1144,6 +1144,7 @@ get_feeds_to_use() fi } +# THIS FUNCTION MAY EXIT (if the determined version is already installed). generate_download_links() { download_links=() @@ -1152,12 +1153,14 @@ generate_download_links() { link_types=() # If generate_akams_links returns false, no fallback to old links. Just terminate. + # This function may also 'exit' (if the determined version is already installed). generate_akams_links || return # Check other feeds only if we haven't been able to find an aka.ms link. if [[ "${#download_links[@]}" -lt 1 ]]; then for feed in ${feeds[@]} do + # generate_regular_links may also 'exit' (if the determined version is already installed). generate_regular_links $feed || return done fi @@ -1174,9 +1177,7 @@ generate_download_links() { done } -# returns: -# 0 - if operation succeeded and the execution should continue as normal -# 1 - if the script has reached a concluding state and the execution should stop. +# THIS FUNCTION MAY EXIT (if the determined version is already installed). generate_akams_links() { local valid_aka_ms_link=true; @@ -1211,15 +1212,10 @@ generate_akams_links() { effective_versions+=($effective_version) link_types+=("aka.ms") - if [[ "$dry_run" == true ]]; then - print_dry_run "$download_link" "" "$effective_version" - return 1 - fi - # Check if the SDK version is already installed. - if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$effective_version"; then + if [[ "$dry_run" != true ]] && is_dotnet_package_installed "$install_root" "$asset_relative_path" "$effective_version"; then say "$asset_name with version '$effective_version' is already installed." - return 1 + exit 0 fi return 0 @@ -1234,11 +1230,9 @@ generate_akams_links() { say_verbose "Falling back to latest.version file approach." } +# THIS FUNCTION MAY EXIT (if the determined version is already installed) # args: # feed - $1 -# returns: -# 0 - if operation succeded and the execution should continue as normal -# 1 - if the script has reached a concluding state and the execution should stop generate_regular_links() { local feed="$1" local valid_legacy_download_link=true @@ -1276,33 +1270,24 @@ generate_regular_links() { say_verbose "Cound not construct a legacy_download_link; omitting..." fi - if [[ "$dry_run" == true ]]; then - print_dry_run "$download_link" "$legacy_download_link" "$effective_version" - return 1 - fi - # Check if the SDK version is already installed. - if is_dotnet_package_installed "$install_root" "$asset_relative_path" "$effective_version"; then + if [[ "$dry_run" != true ]] && is_dotnet_package_installed "$install_root" "$asset_relative_path" "$effective_version"; then say "$asset_name with version '$effective_version' is already installed." - return 1 + exit 0 fi } -# args: -# download_link - $1 -# legacy_download_link - $2 - can be empty -# specific_version print_dry_run() { - local download_link="$1" - local legacy_download_link="$2" - local specific_version="$3" say "Payload URLs:" - say "Primary named payload URL: ${download_link}" - if [ -n "$legacy_download_link" ]; then - say "Legacy named payload URL: ${legacy_download_link}" - fi - repeatable_command="./$script_name --version "\""$specific_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\""" + + for link_index in "${!download_links[@]}" + do + say "URL #$link_index - ${link_types[$link_index]}: ${download_links[$link_index]}" + done + + resolved_version=${specific_versions[0]} + repeatable_command="./$script_name --version "\""$resolved_version"\"" --install-dir "\""$install_root"\"" --architecture "\""$normalized_architecture"\"" --os "\""$normalized_os"\""" if [ ! -z "$normalized_quality" ]; then repeatable_command+=" --quality "\""$normalized_quality"\""" @@ -1321,7 +1306,6 @@ print_dry_run() { fi say "Repeatable invocation: $repeatable_command" - exit 0 } calculate_vars() { @@ -1648,12 +1632,12 @@ fi check_min_reqs calculate_vars +# generate_regular_links call below will 'exit' if the determined version is already installed. generate_download_links - -if [ "$dry_run" = true ]; then - # Don't continue to installation step in dry_run mode. - return +if [[ "$dry_run" = true ]]; then + print_dry_run + exit 0 fi install_dotnet diff --git a/tests/Install-Scripts.Test/GivenThatIWantToInstallDotnetFromAScript.cs b/tests/Install-Scripts.Test/GivenThatIWantToInstallDotnetFromAScript.cs index 28312762fc..6412877b7b 100644 --- a/tests/Install-Scripts.Test/GivenThatIWantToInstallDotnetFromAScript.cs +++ b/tests/Install-Scripts.Test/GivenThatIWantToInstallDotnetFromAScript.cs @@ -204,6 +204,8 @@ public void WhenInstallingTheSdk(string channel, string? quality, string version .Execute(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); // Run dotnet to verify that the version is installed into correct folder. var dotnetArgs = new List { "--info" }; @@ -217,7 +219,7 @@ public void WhenInstallingTheSdk(string channel, string? quality, string version string installPathRegex = "\\[(/private)?" + Regex.Escape(Path.Combine(_sdkInstallationDirectory, "sdk")) + "\\]"; string regex = Regex.Escape(" ") + versionRegex + Regex.Escape(" ") + installPathRegex; dotnetCommandResult.Should().HaveStdOutMatching(regex); - commandResult.Should().NotHaveStdErr(); + dotnetCommandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); } @@ -236,6 +238,8 @@ public void WhenInstallingDotnetRuntime(string channel, string? quality, string .Execute(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); // Run dotnet to verify that the version is installed into correct folder. var dotnetArgs = new List { "--info" }; @@ -249,7 +253,7 @@ public void WhenInstallingDotnetRuntime(string channel, string? quality, string string lineEndRegex = "\\ \\[(/private)?" + Regex.Escape(Path.Combine(_sdkInstallationDirectory, "shared", "Microsoft.NETCore.App")) + "\\]"; string regex = lineStartRegex + versionRegex + lineEndRegex; dotnetCommandResult.Should().HaveStdOutMatching(regex); - commandResult.Should().NotHaveStdErr(); + dotnetCommandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); } @@ -275,6 +279,8 @@ public void WhenInstallingAspNetCoreRuntime(string channel, string? quality, str .Execute(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); // Run dotnet to verify that the version is installed into correct folder. var dotnetArgs = new List { "--info" }; @@ -288,7 +294,7 @@ public void WhenInstallingAspNetCoreRuntime(string channel, string? quality, str string lineEndRegex = "\\ \\[(/private)?" + Regex.Escape(Path.Combine(_sdkInstallationDirectory, "shared", "Microsoft.AspNetCore.App")) + "\\]"; string regex = lineStartRegex + versionRegex + lineEndRegex; dotnetCommandResult.Should().HaveStdOutMatching(regex); - commandResult.Should().NotHaveStdErr(); + dotnetCommandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); } @@ -329,6 +335,7 @@ public void WhenInstallingWindowsdesktopRuntime(string channel, string? quality, commandResult.Should().NotHaveStdErr(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); @@ -353,6 +360,7 @@ public void WhenInstallingTheSdkWithFeedCredential(string channel, string? quali commandResult.Should().NotHaveStdErr(); commandResult.Should().HaveStdOutContaining("Installation finished"); commandResult.Should().NotHaveStdOutContainingIgnoreCase(feedCredential); + commandResult.Should().Pass(); } [Theory] @@ -372,6 +380,7 @@ public void WhenInstallingDotnetRuntimeWithFeedCredential(string channel, string commandResult.Should().NotHaveStdErr(); commandResult.Should().HaveStdOutContaining("Installation finished"); commandResult.Should().NotHaveStdOutContainingIgnoreCase(feedCredential); + commandResult.Should().Pass(); } [Theory] @@ -390,6 +399,8 @@ public void WhenInstallingASpecificVersionOfTheSdk(string version, string? effec .Execute(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); // Run dotnet to verify that the version is installed into correct folder. var dotnetArgs = new List { "--info" }; @@ -403,7 +414,7 @@ public void WhenInstallingASpecificVersionOfTheSdk(string version, string? effec string installPathRegex = "\\[(/private)?" + Regex.Escape(Path.Combine(_sdkInstallationDirectory, "sdk")) + "\\]"; string regex = Regex.Escape(" " + (effectiveVersion ?? version) + " ") + installPathRegex; dotnetCommandResult.Should().HaveStdOutMatching(regex); - commandResult.Should().NotHaveStdErr(); + dotnetCommandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); } @@ -424,6 +435,8 @@ public void WhenInstallingASpecificVersionOfDotnetRuntime(string version, string .Execute(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); // Run dotnet to verify that the version is installed into correct folder. var dotnetArgs = new List { "--info" }; @@ -437,7 +450,7 @@ public void WhenInstallingASpecificVersionOfDotnetRuntime(string version, string string lineEndRegex = "\\ \\[(/private)?" + Regex.Escape(Path.Combine(_sdkInstallationDirectory, "shared", "Microsoft.NETCore.App")) + "\\]"; string regex = lineStartRegex + Regex.Escape(effectiveVersion ?? version) + lineEndRegex; dotnetCommandResult.Should().HaveStdOutMatching(regex); - commandResult.Should().NotHaveStdErr(); + dotnetCommandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); } @@ -458,6 +471,8 @@ public void WhenInstallingASpecificVersionOfAspNetCoreRuntime(string version, st .Execute(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); // Run dotnet to verify that the version is installed into correct folder. var dotnetArgs = new List { "--info" }; @@ -471,7 +486,7 @@ public void WhenInstallingASpecificVersionOfAspNetCoreRuntime(string version, st string lineEndRegex = "\\ \\[(/private)?" + Regex.Escape(Path.Combine(_sdkInstallationDirectory, "shared", "Microsoft.AspNetCore.App")) + "\\]"; string regex = lineStartRegex + Regex.Escape(effectiveVersion ?? version) + lineEndRegex; dotnetCommandResult.Should().HaveStdOutMatching(regex); - commandResult.Should().NotHaveStdErr(); + dotnetCommandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); } @@ -500,6 +515,7 @@ public void WhenInstallingASpecificVersionOfWindowsdesktopRuntime(string version commandResult.Should().NotHaveStdErr(); commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().Pass(); TestOutputHelper.PopulateTestLoggerOutput(outputHelper, commandResult); @@ -507,6 +523,50 @@ public void WhenInstallingASpecificVersionOfWindowsdesktopRuntime(string version // Add the validation once the becomes available in the artifacts. } + [Theory] + [InlineData("5.0.404-servicing.21560.14", "5.0.404")] + [InlineData("6.0.100-preview.6.21364.34")] + [InlineData("7.0.100-alpha.1.22054.9")] + [InlineData("5.0.13-servicing.21552.32", "5.0.13", "aspnetcore")] + [InlineData("6.0.0-preview.4.21176.7", null, "aspnetcore")] + [InlineData("7.0.0-alpha.1.21567.15", null, "aspnetcore")] + [InlineData("5.0.13-servicing.21560.6", "5.0.13", "dotnet")] + [InlineData("6.0.0-preview.4.21176.7", null, "dotnet")] + [InlineData("7.0.0-alpha.1.21528.8", null, "dotnet")] + [InlineData("6.0.1-servicing.21568.2", "6.0.1", "windowsdesktop")] + [InlineData("7.0.0-alpha.1.21472.1", null, "windowsdesktop")] + public void WhenInstallingAnAlreadyInstalledVersion(string version, string? effectiveVersion = null, string? runtime = null) + { + if (runtime == "windowsdesktop" && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Don't install windowsdesktop if not on Windows. + return; + } + + // Run install script to download and install. + var args = GetInstallScriptArgs(channel: null, runtime, quality: null, _sdkInstallationDirectory, version: version); + + var commandResult = CreateInstallCommand(args) + .CaptureStdOut() + .CaptureStdErr() + .Execute(); + + commandResult.Should().HaveStdOutContaining("Installation finished"); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); + + // Run the same command. This time, it should say "already installed". + commandResult = CreateInstallCommand(args) + .CaptureStdOut() + .CaptureStdErr() + .Execute(); + + commandResult.Should().NotHaveStdOutContaining("Installation finished"); + commandResult.Should().HaveStdOutContaining($"with version '{effectiveVersion ?? version}' is already installed."); + commandResult.Should().NotHaveStdErr(); + commandResult.Should().Pass(); + } + [Theory] [InlineData(null, "2.4", "ga")] [InlineData(null, "3.9", null)]