Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 16 additions & 0 deletions src/Cli/dotnet/ToolPackage/ToolPackageDownloaderBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,14 @@ protected void DownloadTool(

CreateAssetFile(packageId, packageVersion, packageDownloadDir, Path.Combine(assetFileDirectory.Value, ToolPackageInstance.AssetsFileName), _runtimeJsonPath, verbosity, targetFramework);

// Delete any stale RID-specific package asset file from a previous installation to prevent it
// from being incorrectly picked up if this package does not need a RID-specific package.
var ridSpecificAssetFilePath = Path.Combine(assetFileDirectory.Value, ToolPackageInstance.RidSpecificPackageAssetsFileName);
if (_fileSystem.File.Exists(ridSpecificAssetFilePath))
{
_fileSystem.File.Delete(ridSpecificAssetFilePath);
}

// Also download RID-specific package if needed
if (ResolveRidSpecificPackage(packageId, packageVersion, packageDownloadDir, assetFileDirectory, verbosity) is PackageId ridSpecificPackage)
{
Expand Down Expand Up @@ -307,6 +315,14 @@ public bool TryGetDownloadedTool(
}
CreateAssetFile(packageId, packageVersion, _localToolDownloadDir, Path.Combine(_localToolAssetDir.Value, ToolPackageInstance.AssetsFileName), _runtimeJsonPath, verbosity, targetFramework);

// Delete any stale RID-specific package asset file from a previous installation to prevent it
// from being incorrectly picked up if this package does not need a RID-specific package.
var ridSpecificAssetFilePath = Path.Combine(_localToolAssetDir.Value, ToolPackageInstance.RidSpecificPackageAssetsFileName);
if (_fileSystem.File.Exists(ridSpecificAssetFilePath))
{
_fileSystem.File.Delete(ridSpecificAssetFilePath);
}

if (ResolveRidSpecificPackage(packageId, packageVersion, _localToolDownloadDir, _localToolAssetDir, verbosity) is PackageId ridSpecificPackage)
{
if (!IsPackageInstalled(ridSpecificPackage, packageVersion, _localToolDownloadDir.Value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1028,5 +1028,63 @@ public void GivenAToolWithHigherFrameworkItShowsAppropriateErrorMessage()
.WithMessage("*requires a higher version of .NET*")
.WithMessage("*.NET 99*");
}

[Fact]
public void GivenStaleRidSpecificAssetFileLocalToolInstallDeletesItAndReturnsCorrectCommand()
{
// Regression test for https://github.com/dotnet/sdk/issues/46059
// When restoring multiple local tools, the first tool (e.g. dotnet-ef) may use a
// RID-specific package and leave a project.assets.ridpackage.json in the shared
// _localToolAssetDir. If the second tool does not need a RID-specific package,
// the stale file must be deleted so ToolPackageInstance reads the correct
// project.assets.json instead of the stale one from the first tool.

var testDir = TestAssetsManager.CreateTestDirectory();
var fileSystem = new FileSystemMockBuilder().Build();
var toolsRoot = new DirectoryPath(testDir.Path).WithSubDirectories("tools");
var storeAndQuery = new ToolPackageStoreAndQuery(toolsRoot, fileSystem);

var downloader = new ExposingLocalToolAssetDirDownloader(
storeAndQuery,
runtimeJsonPathForTests: SdkTestContext.GetRuntimeGraphFilePath(),
currentWorkingDirectory: testDir.Path,
fileSystem);

// Simulate a stale project.assets.ridpackage.json left by a previous tool installation
// (e.g., the first tool in the manifest used RID-specific packages).
var staleFilePath = Path.Combine(
downloader.LocalToolAssetDir.Value,
ToolPackageInstance.RidSpecificPackageAssetsFileName);
fileSystem.File.WriteAllText(staleFilePath, "stale content from a previous tool");
fileSystem.File.Exists(staleFilePath).Should().BeTrue("precondition: stale RID-specific asset file must exist");

// Act: install a local tool (this simulates the second tool in a manifest restore).
var package = downloader.InstallPackage(
new PackageLocation(),
packageId: new PackageId("test.local.tool"),
verbosity: new VerbosityOptions(),
versionRange: VersionRange.Parse(ToolPackageDownloaderMock2.DefaultPackageVersion));

// Assert: the stale file must be deleted so ToolPackageInstance reads the correct assets.
fileSystem.File.Exists(staleFilePath).Should().BeFalse(
"the stale project.assets.ridpackage.json must be cleaned up before creating ToolPackageInstance");

// And the installed package must report the correct command (not from the stale file).
package.Command.Name.Value.Should().Be(ToolPackageDownloaderMock2.DefaultToolCommandName);
}

private sealed class ExposingLocalToolAssetDirDownloader : ToolPackageDownloaderMock2
{
public ExposingLocalToolAssetDirDownloader(
IToolPackageStore store,
string runtimeJsonPathForTests,
string currentWorkingDirectory,
IFileSystem fileSystem)
: base(store, runtimeJsonPathForTests, currentWorkingDirectory, fileSystem)
{
}

public DirectoryPath LocalToolAssetDir => _localToolAssetDir;
}
}
}
Loading