Skip to content

Commit cf044d8

Browse files
nkolev92Copilot
andcommitted
Fix restore source name resolution
Resolve explicit restore sources against enabled package source names from NuGet.Config before treating them as paths. Add regression coverage for nuget.exe, dotnet restore, and MSBuild restore. Co-authored-by: Copilot <[email protected]>
1 parent de3b92c commit cf044d8

8 files changed

Lines changed: 317 additions & 6 deletions

File tree

src/NuGet.Core/NuGet.Build.Tasks/BuildTasksUtility.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -652,12 +652,14 @@ private static IEnumerable<RestoreLogMessage> ProcessFailedEventsIntoRestoreLogs
652652

653653
public static string[] GetSources(string startupDirectory, string projectDirectory, string[] sources, string[] sourcesOverride, IEnumerable<string> additionalProjectSources, ISettings settings)
654654
{
655+
var configuredSources = SettingsUtility.GetEnabledSources(settings).ToList();
656+
655657
// Sources
656658
var currentSources = RestoreSettingsUtils.GetValue(
657-
() => sourcesOverride?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(),
659+
() => sourcesOverride?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => ResolveSourceValue(startupDirectory, e, configuredSources)).ToArray(),
658660
() => MSBuildRestoreUtility.ContainsClearKeyword(sources) ? Array.Empty<string>() : null,
659-
() => sources?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(projectDirectory, e)).ToArray(),
660-
() => (PackageSourceProvider.LoadPackageSources(settings)).Where(e => e.IsEnabled).Select(e => e.Source).ToArray());
661+
() => sources?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => ResolveSourceValue(projectDirectory, e, configuredSources)).ToArray(),
662+
() => configuredSources.Select(e => e.Source).ToArray());
661663

662664
// Append additional sources
663665
// Escape strings to avoid xplat path issues with msbuild.
@@ -669,6 +671,20 @@ public static string[] GetSources(string startupDirectory, string projectDirecto
669671
return AppendItems(projectDirectory, currentSources, filteredAdditionalProjectSources);
670672
}
671673

674+
private static string ResolveSourceValue(string rootDirectory, string source, IReadOnlyList<PackageSource> configuredSources)
675+
{
676+
PackageSource configuredSource = configuredSources.FirstOrDefault(
677+
e => string.Equals(e.Name, source, StringComparison.OrdinalIgnoreCase)
678+
|| string.Equals(e.Source, source, StringComparison.OrdinalIgnoreCase));
679+
680+
if (configuredSource != null)
681+
{
682+
return configuredSource.Source;
683+
}
684+
685+
return UriUtility.GetAbsolutePath(rootDirectory, source);
686+
}
687+
672688
/// <summary>
673689
/// Gets the package fallback folders for a project.
674690
/// </summary>

src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -831,12 +831,14 @@ internal static List<PackageSource> GetSources(IProject project, ISettings setti
831831

832832
private static string[] GetSources(string startupDirectory, string projectDirectory, string[]? sources, string[]? sourcesOverride, IEnumerable<string> additionalProjectSources, ISettings settings)
833833
{
834+
List<PackageSource> configuredSources = SettingsUtility.GetEnabledSources(settings).ToList();
835+
834836
// Sources
835837
var currentSources = GetValue(
836-
() => sourcesOverride?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(),
838+
() => sourcesOverride?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => ResolveSourceValue(startupDirectory, e, configuredSources)).ToArray(),
837839
() => MSBuildRestoreUtility.ContainsClearKeyword(sources) ? Array.Empty<string>() : null,
838-
() => sources?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(projectDirectory, e)).ToArray(),
839-
() => (PackageSourceProvider.LoadPackageSources(settings)).Where(e => e.IsEnabled).Select(e => e.Source).ToArray());
840+
() => sources?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => ResolveSourceValue(projectDirectory, e, configuredSources)).ToArray(),
841+
() => configuredSources.Select(e => e.Source).ToArray());
840842

841843
// Append additional sources
842844
// Escape strings to avoid xplat path issues with msbuild.
@@ -849,6 +851,20 @@ private static string[] GetSources(string startupDirectory, string projectDirect
849851
return AppendItems(projectDirectory, currentSources!, filteredAdditionalProjectSources);
850852
}
851853

854+
private static string ResolveSourceValue(string rootDirectory, string source, IReadOnlyList<PackageSource> configuredSources)
855+
{
856+
PackageSource? configuredSource = configuredSources.FirstOrDefault(
857+
e => string.Equals(e.Name, source, StringComparison.OrdinalIgnoreCase)
858+
|| string.Equals(e.Source, source, StringComparison.OrdinalIgnoreCase));
859+
860+
if (configuredSource != null)
861+
{
862+
return configuredSource.Source;
863+
}
864+
865+
return UriUtility.GetAbsolutePath(rootDirectory, source)!;
866+
}
867+
852868
private static string[] AppendItems(string projectDirectory, string[] current, IEnumerable<string>? additional)
853869
{
854870
if (additional == null || !additional.Any())

test/NuGet.Clients.Tests/NuGet.CommandLine.Test/RestoreNETCoreTest.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,47 @@ public async Task RestoreNetCore_VerifyProjectConfigCanOverrideSolutionConfigAsy
440440
}
441441
}
442442

443+
[Fact]
444+
public async Task RestoreNetCore_WithNuGetExe_WhenSourceArgUsesConfiguredSourceName_RestoresFromConfigSourceAsync()
445+
{
446+
using (var pathContext = new SimpleTestPathContext())
447+
{
448+
var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot);
449+
var project = SimpleTestProjectContext.CreateNETCore(
450+
"projectA",
451+
pathContext.SolutionRoot,
452+
NuGetFramework.Parse("net45"));
453+
454+
var package = new SimpleTestPackageContext()
455+
{
456+
Id = "packageA",
457+
Version = "1.0.0"
458+
};
459+
460+
project.AddPackageToAllFrameworks(package);
461+
project.Properties.Clear();
462+
solution.Projects.Add(project);
463+
solution.Create();
464+
465+
await SimpleTestPackageUtility.CreateFolderFeedV3Async(
466+
pathContext.PackageSource,
467+
PackageSaveMode.Defaultv3,
468+
package);
469+
470+
pathContext.Settings.RemoveSource("source");
471+
pathContext.Settings.AddSource("source_name", pathContext.PackageSource);
472+
473+
var result = Util.Restore(
474+
pathContext,
475+
project.ProjectPath,
476+
expectedExitCode: 0,
477+
additionalArgs: $"-Source source_name -ConfigFile \"{pathContext.Settings.ConfigPath}\"");
478+
479+
result.Success.Should().BeTrue(because: result.AllOutput);
480+
project.AssetsFile.Libraries.Select(e => e.Name).Should().Contain(package.Id);
481+
}
482+
}
483+
443484
[Fact]
444485
public async Task RestoreNetCore_VerifyProjectConfigChangeTriggersARestoreAsync()
445486
{

test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRestoreTests.cs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,58 @@ public void DotnetRestore_SolutionRestoreVerifySolutionDirPassedToProjects()
100100
}
101101
}
102102

103+
[Fact]
104+
public async Task DotnetRestore_WhenSourceUsesConfiguredSourceName_RestoresFromConfigSource()
105+
{
106+
using (SimpleTestPathContext pathContext = _dotnetFixture.CreateSimpleTestPathContext())
107+
{
108+
var package = new SimpleTestPackageContext()
109+
{
110+
Id = "TestPackage",
111+
Version = "1.0.0"
112+
};
113+
114+
await SimpleTestPackageUtility.CreateFolderFeedV3Async(
115+
pathContext.PackageSource,
116+
PackageSaveMode.Defaultv3,
117+
package);
118+
119+
pathContext.Settings.RemoveSource("source");
120+
pathContext.Settings.AddSource("source_name", pathContext.PackageSource);
121+
122+
var projectName = "ClassLibrary1";
123+
var workingDirectory = Path.Combine(pathContext.SolutionRoot, projectName);
124+
var projectFile = Path.Combine(workingDirectory, $"{projectName}.csproj");
125+
126+
_dotnetFixture.CreateDotnetNewProject(pathContext.SolutionRoot, projectName, "classlib -f netstandard2.0", testOutputHelper: _testOutputHelper);
127+
128+
using (var stream = File.Open(projectFile, FileMode.Open, FileAccess.ReadWrite))
129+
{
130+
var xml = XDocument.Load(stream);
131+
132+
var attributes = new Dictionary<string, string>() { { "Version", package.Version } };
133+
134+
ProjectFileUtils.AddItem(
135+
xml,
136+
"PackageReference",
137+
package.Id,
138+
string.Empty,
139+
new Dictionary<string, string>(),
140+
attributes);
141+
142+
ProjectFileUtils.WriteXmlToFile(xml, stream);
143+
}
144+
145+
var result = _dotnetFixture.RunDotnetExpectSuccess(
146+
workingDirectory,
147+
$"restore {projectName}.csproj --source source_name --configfile \"{pathContext.Settings.ConfigPath}\"",
148+
testOutputHelper: _testOutputHelper);
149+
150+
result.ExitCode.Should().Be(0, because: result.AllOutput);
151+
File.Exists(Path.Combine(workingDirectory, "obj", "project.assets.json")).Should().BeTrue(because: result.AllOutput);
152+
}
153+
}
154+
103155
[Fact]
104156
public void DotnetRestore_WithAuthorSignedPackage_Succeeds()
105157
{

test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,74 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async(
740740
}
741741
}
742742

743+
[PlatformTheory(Platform.Windows)]
744+
[InlineData(true, true)]
745+
[InlineData(true, false)]
746+
[InlineData(false, false)]
747+
public async Task MsbuildRestore_WithConfiguredSourceName_ResolvesFromConfig(bool isStaticGraphRestore, bool usePackageSpecFactory)
748+
{
749+
// Arrange
750+
using (var pathContext = new SimpleTestPathContext())
751+
{
752+
// Set up solution, project, and packages
753+
var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot);
754+
755+
var net461 = NuGetFramework.Parse("net472");
756+
757+
var project = SimpleTestProjectContext.CreateLegacyPackageReference(
758+
"a",
759+
pathContext.SolutionRoot,
760+
net461);
761+
762+
var packageX = new SimpleTestPackageContext()
763+
{
764+
Id = "x",
765+
Version = "1.0.0"
766+
};
767+
768+
packageX.Files.Clear();
769+
project.AddPackageToAllFrameworks(packageX);
770+
packageX.AddFile("lib/net472/a.dll");
771+
772+
solution.Projects.Add(project);
773+
solution.Create();
774+
775+
await SimpleTestPackageUtility.CreateFolderFeedV3Async(
776+
pathContext.PackageSource,
777+
packageX);
778+
779+
pathContext.Settings.RemoveSource("source");
780+
pathContext.Settings.AddSource("source_name", pathContext.PackageSource);
781+
782+
var projectOutputPaths = new[]
783+
{
784+
project.AssetsFileOutputPath,
785+
project.PropsOutput,
786+
project.TargetsOutput,
787+
project.CacheFileOutputPath,
788+
};
789+
790+
var environmentVariables = new Dictionary<string, string>();
791+
environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables);
792+
environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString();
793+
794+
var result = _msbuildFixture.RunMsBuild(
795+
pathContext.WorkingDirectory,
796+
$"/t:restore {project.ProjectPath} /p:RestoreSources=\"source_name\" /p:RestoreConfigFile=\"{pathContext.Settings.ConfigPath}\"" +
797+
(isStaticGraphRestore ? " /p:RestoreUseStaticGraphEvaluation=true" : string.Empty),
798+
ignoreExitCode: true,
799+
testOutputHelper: _testOutputHelper,
800+
environmentVariables);
801+
802+
result.Success.Should().BeTrue(because: result.AllOutput);
803+
804+
foreach (var asset in projectOutputPaths)
805+
{
806+
new FileInfo(asset).Exists.Should().BeTrue(because: result.AllOutput);
807+
}
808+
}
809+
}
810+
743811
[PlatformFact(Platform.Windows)]
744812
public Task MsbuildRestore_WithStaticGraphRestore_MessageLoggedAtDefaultVerbosityWhenThereAreNoProjectsToRestore()
745813
{

test/NuGet.Core.Tests/NuGet.Build.Tasks.Console.Test/BuildTasksUtilityTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,46 @@ public void GetSources_WithRestoreSourcesGlobal_Property_ResolvesAgainstWorkingD
135135
}
136136
}
137137

138+
[Fact]
139+
public void GetSources_WithConfiguredSourceName_UsesConfiguredSourceValue()
140+
{
141+
using (var testDir = TestDirectory.CreateInTemp())
142+
{
143+
// Arrange
144+
var startupDirectory = Path.Combine(testDir, "startup");
145+
var projectDirectory = Path.Combine(testDir, "project");
146+
var configuredSource = "https://configured-source/v3/index.json";
147+
Directory.CreateDirectory(projectDirectory);
148+
File.WriteAllText(
149+
Path.Combine(projectDirectory, Settings.DefaultSettingsFileName),
150+
$@"<?xml version=""1.0"" encoding=""utf-8""?>
151+
<configuration>
152+
<packageSources>
153+
<clear />
154+
<add key=""source_name"" value=""{configuredSource}"" />
155+
</packageSources>
156+
</configuration>");
157+
158+
var settings = Settings.LoadDefaultSettings(
159+
root: projectDirectory,
160+
configFileName: null,
161+
machineWideSettings: null);
162+
163+
// Act
164+
var effectiveSources = BuildTasksUtility.GetSources(
165+
startupDirectory: startupDirectory,
166+
projectDirectory: projectDirectory,
167+
sources: new[] { "source_name" },
168+
sourcesOverride: new[] { "source_name" },
169+
additionalProjectSources: Array.Empty<string>(),
170+
settings: settings
171+
);
172+
173+
// Assert
174+
effectiveSources.Should().BeEquivalentTo(new[] { configuredSource });
175+
}
176+
}
177+
138178
[Theory]
139179
[InlineData("0", "false", 0)]
140180
[InlineData("0", "true", 0)]

test/NuGet.Core.Tests/NuGet.Build.Tasks.Console.Test/MSBuildStaticGraphRestoreTests.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -696,6 +696,46 @@ public void GetSources_WhenRestoreSourcesAndRestoreSourcesOverrideSpecified_Corr
696696
});
697697
}
698698

699+
[Fact]
700+
public void GetSources_WhenRestoreSourcesMatchesConfiguredSourceName_UsesConfiguredSourceValue()
701+
{
702+
using (var testDir = TestDirectory.CreateInTemp())
703+
{
704+
var configuredSource = "https://configured-source/v3/index.json";
705+
var project = new MockMSBuildProject(
706+
properties: new Dictionary<string, string>
707+
{
708+
["RestoreSources"] = "source_name",
709+
},
710+
globalProperties: new Dictionary<string, string>
711+
{
712+
["RestoreSources"] = "source_name"
713+
});
714+
715+
File.WriteAllText(
716+
Path.Combine(testDir, Settings.DefaultSettingsFileName),
717+
$@"<?xml version=""1.0"" encoding=""utf-8""?>
718+
<configuration>
719+
<packageSources>
720+
<clear />
721+
<add key=""source_name"" value=""{configuredSource}"" />
722+
</packageSources>
723+
</configuration>");
724+
725+
var settings = Settings.LoadDefaultSettings(
726+
root: testDir,
727+
configFileName: null,
728+
machineWideSettings: null);
729+
730+
var actual = MSBuildStaticGraphRestore.GetSources(project, new[] { project }, settings);
731+
732+
actual.Should().BeEquivalentTo(new[]
733+
{
734+
new PackageSource(configuredSource),
735+
});
736+
}
737+
}
738+
699739
[Fact]
700740
public void GetSources_WhenRestoreSourcesSpecified_CorrectSourcesDetected()
701741
{

test/NuGet.Core.Tests/NuGet.Build.Tasks.Test/GetRestoreSettingTaskTests.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,44 @@ public void GetRestoreSettingsTask_WithRestoreSourcesOverride_ResolvesAgainstWor
677677
}
678678
}
679679

680+
[Fact]
681+
public void GetRestoreSettingsTask_WithRestoreSourcesOverrideAndConfiguredSourceName_UsesConfiguredSourceValue()
682+
{
683+
using (var testDir = TestDirectory.CreateInTemp())
684+
{
685+
// Arrange
686+
var buildEngine = new TestBuildEngine();
687+
var startupDirectory = Path.Combine(testDir, "innerPath");
688+
var configuredSource = "https://configured-source/v3/index.json";
689+
690+
File.WriteAllText(
691+
Path.Combine(testDir, Settings.DefaultSettingsFileName),
692+
$@"<?xml version=""1.0"" encoding=""utf-8""?>
693+
<configuration>
694+
<packageSources>
695+
<clear />
696+
<add key=""source_name"" value=""{configuredSource}"" />
697+
</packageSources>
698+
</configuration>");
699+
700+
var task = new GetRestoreSettingsTask()
701+
{
702+
BuildEngine = buildEngine,
703+
MSBuildStartupDirectory = startupDirectory,
704+
ProjectUniqueName = Path.Combine(testDir, "a.csproj"),
705+
RestoreSourcesOverride = new[] { "source_name" },
706+
RestoreSettingsPerFramework = Array.Empty<ITaskItem>()
707+
};
708+
709+
// Act
710+
var result = task.Execute();
711+
712+
// Assert
713+
result.Should().BeTrue();
714+
task.OutputSources.Should().BeEquivalentTo(new[] { configuredSource });
715+
}
716+
}
717+
680718
[Fact]
681719
public void GetRestoreSettingsTask_WithFallbackFoldersOverride_ResolvesAgainstWorkingDirectory()
682720
{

0 commit comments

Comments
 (0)