Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Remove GetRuntimeVersionFromArchive and add FileVersionInfo checks in…
… tests

- Remove GetRuntimeVersionFromArchive and helper methods (DecompressTarGzToTemp, GetRuntimeVersionFromZipEntries, GetRuntimeVersionFromTarEntries)
- Use GetLatestRuntimeVersionFromInstallRoot for version comparison after extraction
- Add FileVersionInfo checks in tests on Windows to verify muxer version upgrades/downgrades
- Add GetMuxerFileVersion helper method in tests
- Add using statements for System.Diagnostics and System.Runtime.InteropServices

Note: Test MuxerIsNotDowngraded_WhenInstallingOlderSdk now fails because when both runtime 9.0 and 10.0 are present after installing SDK 9.0, GetLatestRuntimeVersionFromInstallRoot returns 10.0 for both existingMuxerVersion and newMuxerVersion, causing the condition (newMuxerVersion < existingMuxerVersion) to be false, so the 9.0 muxer is kept instead of restoring the 10.0 muxer.

Co-authored-by: dsplaisted <[email protected]>
  • Loading branch information
Copilot and dsplaisted committed Dec 5, 2025
commit c8647dc3705ce1cac4930ebb3c99130701d3ee8e
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,7 @@ private void ExtractArchiveDirectlyToTarget(string archivePath, string targetDir
// Step 4: If there was a previous muxer, compare versions and restore if needed
if (hadExistingMuxer && File.Exists(muxerTempPath))
{
// Get the runtime version from the archive we just extracted
Version? newMuxerVersion = GetRuntimeVersionFromArchive(archivePath);
Version? newMuxerVersion = GetLatestRuntimeVersionFromInstallRoot(targetDir);

// If the new version is less than the old version, restore the old muxer
if (newMuxerVersion != null && existingMuxerVersion != null && newMuxerVersion < existingMuxerVersion)
Expand Down Expand Up @@ -160,124 +159,6 @@ private void ExtractArchiveDirectlyToTarget(string archivePath, string targetDir
return highestVersion;
}

/// <summary>
/// Gets the runtime version from an archive by examining the runtime directories.
/// </summary>
private static Version? GetRuntimeVersionFromArchive(string archivePath)
{
if (archivePath.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
{
using var zip = ZipFile.OpenRead(archivePath);
return GetRuntimeVersionFromZipEntries(zip.Entries);
}
else if (archivePath.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase) || archivePath.EndsWith(".tar", StringComparison.OrdinalIgnoreCase))
{
string tarPath = archivePath;
bool needsCleanup = false;

if (archivePath.EndsWith(".gz", StringComparison.OrdinalIgnoreCase))
{
tarPath = DecompressTarGzToTemp(archivePath);
needsCleanup = true;
}

try
{
using var tarStream = File.OpenRead(tarPath);
using var tarReader = new TarReader(tarStream);
return GetRuntimeVersionFromTarEntries(tarReader);
}
finally
{
if (needsCleanup && File.Exists(tarPath))
{
File.Delete(tarPath);
}
}
}

return null;
}

/// <summary>
/// Decompresses a .tar.gz file to a temporary location.
/// </summary>
private static string DecompressTarGzToTemp(string gzPath)
{
string tempPath = Path.Combine(Path.GetTempPath(), $"dotnet-{Guid.NewGuid()}.tar");
using FileStream originalFileStream = File.OpenRead(gzPath);
using FileStream decompressedFileStream = File.Create(tempPath);
using GZipStream decompressionStream = new GZipStream(originalFileStream, CompressionMode.Decompress);
decompressionStream.CopyTo(decompressedFileStream);
return tempPath;
}

/// <summary>
/// Gets the runtime version from zip archive entries.
/// </summary>
private static Version? GetRuntimeVersionFromZipEntries(IEnumerable<ZipArchiveEntry> entries)
{
Version? highestVersion = null;

foreach (var entry in entries)
{
// Look for shared/Microsoft.NETCore.App/{version}/ pattern
if (entry.FullName.Contains("shared/Microsoft.NETCore.App/", StringComparison.OrdinalIgnoreCase) ||
entry.FullName.Contains("shared\\Microsoft.NETCore.App\\", StringComparison.OrdinalIgnoreCase))
{
var parts = entry.FullName.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
var appIndex = Array.FindIndex(parts, p => p.Equals("Microsoft.NETCore.App", StringComparison.OrdinalIgnoreCase));

if (appIndex >= 0 && appIndex + 1 < parts.Length)
{
var versionString = parts[appIndex + 1];
if (Version.TryParse(versionString, out Version? version))
{
if (highestVersion == null || version > highestVersion)
{
highestVersion = version;
}
}
}
}
}

return highestVersion;
}

/// <summary>
/// Gets the runtime version from tar archive entries.
/// </summary>
private static Version? GetRuntimeVersionFromTarEntries(TarReader tarReader)
{
Version? highestVersion = null;
TarEntry? entry;

while ((entry = tarReader.GetNextEntry()) is not null)
{
// Look for shared/Microsoft.NETCore.App/{version}/ pattern
if (entry.Name.Contains("shared/Microsoft.NETCore.App/", StringComparison.OrdinalIgnoreCase))
{
var parts = entry.Name.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
var appIndex = Array.FindIndex(parts, p => p.Equals("Microsoft.NETCore.App", StringComparison.OrdinalIgnoreCase));

if (appIndex >= 0 && appIndex + 1 < parts.Length)
{
var versionString = parts[appIndex + 1];
if (Version.TryParse(versionString, out Version? version))
{
if (highestVersion == null || version > highestVersion)
{
highestVersion = version;
}
}
}
}
}

return highestVersion;
}

/// <summary>
/// Extracts a tar or tar.gz archive to the target directory.
/// </summary>
Expand Down
76 changes: 76 additions & 0 deletions test/dotnetup.Tests/LibraryTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Dotnet.Installation;
using Microsoft.Dotnet.Installation.Internal;
Expand Down Expand Up @@ -87,6 +89,13 @@ public void MuxerIsUpdated_WhenInstallingNewerSdk()
var muxerSizeAfterSdk9 = new FileInfo(muxerPath).Length;
Log.WriteLine($"Muxer after SDK 9.0 install - Size: {muxerSizeAfterSdk9}, Hash: {muxerHashAfterSdk9}");

Version? fileVersionAfterSdk9 = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
fileVersionAfterSdk9 = GetMuxerFileVersion(muxerPath);
Log.WriteLine($"Muxer FileVersion after SDK 9.0 install: {fileVersionAfterSdk9}");
}

// Install .NET SDK 10.0 second
var sdk10Version = releaseInfoProvider.GetLatestVersion(InstallComponent.SDK, "10.0");
Log.WriteLine($"Installing .NET SDK 10.0: {sdk10Version}");
Expand All @@ -99,8 +108,21 @@ public void MuxerIsUpdated_WhenInstallingNewerSdk()
var muxerSizeAfterSdk10 = new FileInfo(muxerPath).Length;
Log.WriteLine($"Muxer after SDK 10.0 install - Size: {muxerSizeAfterSdk10}, Hash: {muxerHashAfterSdk10}");

Version? fileVersionAfterSdk10 = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
fileVersionAfterSdk10 = GetMuxerFileVersion(muxerPath);
Log.WriteLine($"Muxer FileVersion after SDK 10.0 install: {fileVersionAfterSdk10}");
}

// Verify muxer was updated (file changed)
muxerHashAfterSdk10.Should().NotBe(muxerHashAfterSdk9, "muxer file should be updated when installing newer SDK");

// On Windows, also verify FileVersion was upgraded
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && fileVersionAfterSdk9 != null && fileVersionAfterSdk10 != null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's assert that on Windows the file version before and after the upgrade should not be null, instead of skipping the test if they are null.

Same thing for the other test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Changed both tests to assert that file versions are not null on Windows instead of skipping the assertions:

  • Added fileVersionAfterSdk9.Should().NotBeNull() and fileVersionAfterSdk10.Should().NotBeNull() assertions
  • Removed the null checks from the conditional (&& fileVersionAfterSdk9 != null && fileVersionAfterSdk10 != null)
  • Now the tests will fail on Windows if file versions can't be read

Commit: 5d66e54

{
fileVersionAfterSdk10.Should().BeGreaterThan(fileVersionAfterSdk9, "muxer FileVersion should be upgraded when installing newer SDK");
}
}

[Fact]
Expand Down Expand Up @@ -128,6 +150,13 @@ public void MuxerIsNotDowngraded_WhenInstallingOlderSdk()
var muxerSizeAfterSdk10 = new FileInfo(muxerPath).Length;
Log.WriteLine($"Muxer after SDK 10.0 install - Size: {muxerSizeAfterSdk10}, Hash: {muxerHashAfterSdk10}");

Version? fileVersionAfterSdk10 = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
fileVersionAfterSdk10 = GetMuxerFileVersion(muxerPath);
Log.WriteLine($"Muxer FileVersion after SDK 10.0 install: {fileVersionAfterSdk10}");
}

// Install .NET SDK 9.0 second
var sdk9Version = releaseInfoProvider.GetLatestVersion(InstallComponent.SDK, "9.0");
Log.WriteLine($"Installing .NET SDK 9.0: {sdk9Version}");
Expand All @@ -140,9 +169,22 @@ public void MuxerIsNotDowngraded_WhenInstallingOlderSdk()
var muxerSizeAfterSdk9 = new FileInfo(muxerPath).Length;
Log.WriteLine($"Muxer after SDK 9.0 install - Size: {muxerSizeAfterSdk9}, Hash: {muxerHashAfterSdk9}");

Version? fileVersionAfterSdk9 = null;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
fileVersionAfterSdk9 = GetMuxerFileVersion(muxerPath);
Log.WriteLine($"Muxer FileVersion after SDK 9.0 install: {fileVersionAfterSdk9}");
}

// Verify muxer was NOT downgraded (file unchanged)
muxerHashAfterSdk9.Should().Be(muxerHashAfterSdk10, "muxer file should not be downgraded when installing older SDK");
muxerSizeAfterSdk9.Should().Be(muxerSizeAfterSdk10, "muxer file size should not change when installing older SDK");

// On Windows, also verify FileVersion was not downgraded
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && fileVersionAfterSdk9 != null && fileVersionAfterSdk10 != null)
{
fileVersionAfterSdk9.Should().Be(fileVersionAfterSdk10, "muxer FileVersion should not be downgraded when installing older SDK");
}
}

private static string GetFileHash(string filePath)
Expand All @@ -152,4 +194,38 @@ private static string GetFileHash(string filePath)
var hash = sha256.ComputeHash(stream);
return BitConverter.ToString(hash).Replace("-", "");
}

private static Version? GetMuxerFileVersion(string muxerPath)
{
if (!File.Exists(muxerPath))
{
return null;
}

try
{
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(muxerPath);
if (versionInfo.FileVersion is not null && Version.TryParse(versionInfo.FileVersion, out Version? version))
{
return version;
}

// Fallback to constructing version from individual parts
if (versionInfo.FileMajorPart != 0 || versionInfo.FileMinorPart != 0 ||
versionInfo.FileBuildPart != 0 || versionInfo.FilePrivatePart != 0)
{
return new Version(
versionInfo.FileMajorPart,
versionInfo.FileMinorPart,
versionInfo.FileBuildPart,
versionInfo.FilePrivatePart);
}

return null;
}
catch
{
return null;
}
}
}
Loading