-
Notifications
You must be signed in to change notification settings - Fork 843
Fix default NuGet packages restore folder #15416
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,6 @@ | |
|
|
||
| using System.Collections.Immutable; | ||
| using System.CommandLine; | ||
| using System.Globalization; | ||
| using NuGet.Commands; | ||
| using NuGet.Configuration; | ||
| using NuGet.Frameworks; | ||
|
|
@@ -100,13 +99,6 @@ public static Command Create() | |
| var noNugetOrg = parseResult.GetValue(noNugetOrgOption); | ||
| var verbose = parseResult.GetValue(verboseOption); | ||
|
|
||
| // Validate that both nuget-config and sources aren't provided together | ||
| if (!string.IsNullOrEmpty(nugetConfigPath) && sources.Length > 0) | ||
| { | ||
| Console.Error.WriteLine("Error: Cannot specify both --nuget-config and --source. Use one or the other."); | ||
| return 1; | ||
| } | ||
|
|
||
| // Parse packages (format: PackageId,Version) | ||
| var packages = new List<(string Id, string Version)>(); | ||
| foreach (var pkgArg in packageArgs) | ||
|
|
@@ -124,18 +116,18 @@ public static Command Create() | |
| packages.Add((parts[0], parts[1])); | ||
| } | ||
|
|
||
| return await ExecuteRestoreAsync([.. packages], framework, output, packagesDir, sources, nugetConfigPath, workingDir, noNugetOrg, verbose).ConfigureAwait(false); | ||
| return await ExecuteRestoreAsync(packages, framework, output, packagesDir, sources, nugetConfigPath, workingDir, noNugetOrg, verbose).ConfigureAwait(false); | ||
| }); | ||
|
|
||
| return command; | ||
| } | ||
|
|
||
| private static async Task<int> ExecuteRestoreAsync( | ||
| (string Id, string Version)[] packages, | ||
| List<(string Id, string Version)> packages, | ||
| string framework, | ||
| string output, | ||
| string? packagesDir, | ||
| string[] sources, | ||
| string[] cliSources, | ||
| string? nugetConfigPath, | ||
| string? workingDir, | ||
| bool noNugetOrg, | ||
|
|
@@ -144,19 +136,16 @@ private static async Task<int> ExecuteRestoreAsync( | |
| var outputPath = Path.GetFullPath(output); | ||
| Directory.CreateDirectory(outputPath); | ||
|
|
||
| packagesDir ??= Path.Combine( | ||
| Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), | ||
| ".nuget", "packages"); | ||
|
|
||
| var logger = new NuGetLogger(verbose); | ||
|
|
||
| try | ||
| { | ||
| var nugetFramework = NuGetFramework.Parse(framework); | ||
| // Load NuGet settings once — handles working dir, config file, and machine-wide settings. | ||
| var settings = Settings.LoadDefaultSettings(workingDir, nugetConfigPath, new XPlatMachineWideSetting()); | ||
|
|
||
| if (verbose) | ||
| { | ||
| Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Restoring {0} packages for {1}", packages.Length, framework)); | ||
| Console.WriteLine($"Restoring {packages.Count} packages for {framework}"); | ||
| Console.WriteLine($"Output: {outputPath}"); | ||
| Console.WriteLine($"Packages: {packagesDir}"); | ||
| if (workingDir is not null) | ||
|
|
@@ -169,45 +158,43 @@ private static async Task<int> ExecuteRestoreAsync( | |
| } | ||
| } | ||
|
|
||
| // Load package sources | ||
| var packageSources = LoadPackageSources(sources, nugetConfigPath, workingDir, noNugetOrg, verbose); | ||
| // Resolve the default packages path from settings (env var, config, or ~/.nuget/packages). | ||
| // If --packages-dir is provided, RestoreArgs.GlobalPackagesFolder overrides this. | ||
| var defaultPackagesPath = SettingsUtility.GetGlobalPackagesFolder(settings); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: Above we are printing packagesDir for verbose logging but we are not printing defaultPackagesPath which will be the one we want most of the time. |
||
|
|
||
| // Build PackageSpec | ||
| var packageSpec = BuildPackageSpec(packages, nugetFramework, outputPath, packagesDir, packageSources); | ||
| // Resolve package sources using NuGet's PackageSourceProvider | ||
| var packageSources = ResolvePackageSources(settings, cliSources, noNugetOrg); | ||
|
|
||
| var nugetFramework = NuGetFramework.Parse(framework); | ||
|
|
||
| // Build PackageSpec and DependencyGraphSpec | ||
| var packageSpec = BuildPackageSpec(packages, nugetFramework, outputPath, defaultPackagesPath, packageSources, settings); | ||
|
|
||
| // Create DependencyGraphSpec | ||
| var dgSpec = new DependencyGraphSpec(); | ||
| dgSpec.AddProject(packageSpec); | ||
| dgSpec.AddRestore(packageSpec.RestoreMetadata.ProjectUniqueName); | ||
|
|
||
| // Setup providers | ||
| // Pass settings to the provider so it reuses our pre-loaded settings | ||
| var providerCache = new RestoreCommandProvidersCache(); | ||
| var providers = new List<IPreLoadedRestoreRequestProvider> | ||
| { | ||
| new DependencyGraphSpecRequestProvider(providerCache, dgSpec) | ||
| }; | ||
| var dgProvider = new DependencyGraphSpecRequestProvider(providerCache, dgSpec, settings); | ||
|
|
||
| // Run restore | ||
| // Run restore — let NuGet handle source credentials, parallel execution, etc. | ||
| using var cacheContext = new SourceCacheContext(); | ||
| var restoreContext = new RestoreArgs | ||
| var restoreArgs = new RestoreArgs | ||
| { | ||
| CacheContext = cacheContext, | ||
| Log = logger, | ||
| PreLoadedRequestProviders = providers, | ||
| PreLoadedRequestProviders = [dgProvider], | ||
| DisableParallel = Environment.ProcessorCount == 1, | ||
| AllowNoOp = false, | ||
| GlobalPackagesFolder = packagesDir | ||
| GlobalPackagesFolder = packagesDir, | ||
| MachineWideSettings = new XPlatMachineWideSetting(), | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. NIT: I believe you call this above, so perhaps we can just reuse. |
||
| }; | ||
|
|
||
| if (verbose) | ||
| { | ||
| Console.WriteLine("Running restore..."); | ||
| } | ||
|
|
||
| var results = await RestoreRunner.RunAsync(restoreContext).ConfigureAwait(false); | ||
| var results = await RestoreRunner.RunAsync(restoreArgs).ConfigureAwait(false); | ||
| var summary = results.Count > 0 ? results[0] : null; | ||
|
|
||
| if (summary == null) | ||
| if (summary is null) | ||
| { | ||
| Console.Error.WriteLine("Error: Restore returned no results"); | ||
| return 1; | ||
|
|
@@ -238,108 +225,47 @@ private static async Task<int> ExecuteRestoreAsync( | |
| } | ||
| } | ||
|
|
||
| private static List<PackageSource> LoadPackageSources(string[] sources, string? nugetConfigPath, string? workingDir, bool noNugetOrg, bool verbose) | ||
| private static List<PackageSource> ResolvePackageSources(ISettings settings, string[] cliSources, bool noNugetOrg) | ||
| { | ||
| var packageSources = new List<PackageSource>(); | ||
| // Load enabled sources from NuGet config | ||
| var provider = new PackageSourceProvider(settings); | ||
| var sources = provider.LoadPackageSources().Where(s => s.IsEnabled).ToList(); | ||
|
|
||
| // Add explicit sources first (they get priority) | ||
| foreach (var source in sources) | ||
| // Append CLI --source values (matching NuGet's behavior of merging, not replacing) | ||
| foreach (var cliSource in cliSources) | ||
| { | ||
| packageSources.Add(new PackageSource(source)); | ||
| } | ||
|
|
||
| // Load from specific config file if specified | ||
| if (!string.IsNullOrEmpty(nugetConfigPath) && File.Exists(nugetConfigPath)) | ||
| { | ||
| var configDir = Path.GetDirectoryName(nugetConfigPath)!; | ||
| var configFile = Path.GetFileName(nugetConfigPath); | ||
| var settings = Settings.LoadSpecificSettings(configDir, configFile); | ||
| var provider = new PackageSourceProvider(settings); | ||
|
|
||
| foreach (var source in provider.LoadPackageSources()) | ||
| if (!sources.Any(s => s.Source.Equals(cliSource, StringComparison.OrdinalIgnoreCase))) | ||
| { | ||
| if (source.IsEnabled && !packageSources.Any(s => s.Source == source.Source)) | ||
| { | ||
| packageSources.Add(source); | ||
| } | ||
| } | ||
| } | ||
| // Auto-discover nuget.config from working directory if specified | ||
| else if (!string.IsNullOrEmpty(workingDir) && Directory.Exists(workingDir)) | ||
| { | ||
| try | ||
| { | ||
| // LoadDefaultSettings walks up the directory tree looking for nuget.config files | ||
| var settings = Settings.LoadDefaultSettings(workingDir); | ||
| var provider = new PackageSourceProvider(settings); | ||
|
|
||
| if (verbose) | ||
| { | ||
| // Show the config file paths that were loaded | ||
| var configPaths = settings.GetConfigFilePaths(); | ||
| Console.WriteLine($"Discovering NuGet config from: {workingDir}"); | ||
| foreach (var configPath in configPaths) | ||
| { | ||
| Console.WriteLine($" Loaded config: {configPath}"); | ||
| } | ||
| } | ||
|
|
||
| foreach (var source in provider.LoadPackageSources()) | ||
| { | ||
| if (source.IsEnabled && !packageSources.Any(s => s.Source == source.Source)) | ||
| { | ||
| if (verbose) | ||
| { | ||
| Console.WriteLine($" Discovered source: {source.Name ?? source.Source}"); | ||
| } | ||
| packageSources.Add(source); | ||
| } | ||
| } | ||
| } | ||
| catch (Exception ex) | ||
| { | ||
| if (verbose) | ||
| { | ||
| Console.WriteLine($"Warning: Failed to load NuGet config from {workingDir}: {ex.ToString()}"); | ||
| } | ||
| sources.Add(new PackageSource(cliSource)); | ||
| } | ||
| } | ||
|
|
||
| // Add nuget.org as a fallback source unless opted out | ||
| if (!noNugetOrg) | ||
| { | ||
| const string nugetOrgUrl = "https://api.nuget.org/v3/index.json"; | ||
| if (!packageSources.Any(s => s.Source.Equals(nugetOrgUrl, StringComparison.OrdinalIgnoreCase))) | ||
| if (!sources.Any(s => s.Source.Equals(nugetOrgUrl, StringComparison.OrdinalIgnoreCase))) | ||
| { | ||
| Console.WriteLine("Note: Adding nuget.org as fallback package source. Use --no-nuget-org to disable."); | ||
| packageSources.Add(new PackageSource(nugetOrgUrl, "nuget.org")); | ||
| } | ||
| } | ||
|
|
||
| if (verbose) | ||
| { | ||
| Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "Using {0} package sources:", packageSources.Count)); | ||
| foreach (var source in packageSources) | ||
| { | ||
| Console.WriteLine($" - {source.Name ?? source.Source}"); | ||
| sources.Add(new PackageSource(nugetOrgUrl, "nuget.org")); | ||
| } | ||
| } | ||
|
|
||
| return packageSources; | ||
| return sources; | ||
| } | ||
|
|
||
| private static PackageSpec BuildPackageSpec( | ||
| (string Id, string Version)[] packages, | ||
| List<(string Id, string Version)> packages, | ||
| NuGetFramework framework, | ||
| string outputPath, | ||
| string packagesPath, | ||
| List<PackageSource> sources) | ||
| List<PackageSource> sources, | ||
| ISettings settings) | ||
| { | ||
| var projectName = "AspireRestore"; | ||
| var projectPath = Path.Combine(outputPath, "project.json"); | ||
| var tfmShort = framework.GetShortFolderName(); | ||
|
|
||
| // Build dependencies | ||
| var dependencies = packages.Select(p => new LibraryDependency | ||
| { | ||
| LibraryRange = new LibraryRange( | ||
|
|
@@ -348,15 +274,13 @@ private static PackageSpec BuildPackageSpec( | |
| LibraryDependencyTarget.Package) | ||
| }).ToImmutableArray(); | ||
|
|
||
| // Build target framework info | ||
| var tfInfo = new TargetFrameworkInformation | ||
| { | ||
| FrameworkName = framework, | ||
| TargetAlias = tfmShort, | ||
| Dependencies = dependencies | ||
| }; | ||
|
|
||
| // Build restore metadata | ||
| var restoreMetadata = new ProjectRestoreMetadata | ||
| { | ||
| ProjectUniqueName = projectName, | ||
|
|
@@ -366,15 +290,14 @@ private static PackageSpec BuildPackageSpec( | |
| OutputPath = outputPath, | ||
| PackagesPath = packagesPath, | ||
| OriginalTargetFrameworks = [tfmShort], | ||
| ConfigFilePaths = settings.GetConfigFilePaths().ToList(), | ||
| }; | ||
|
|
||
| // Add sources | ||
| foreach (var source in sources) | ||
| { | ||
| restoreMetadata.Sources.Add(source); | ||
| } | ||
|
|
||
| // Add target framework | ||
| restoreMetadata.TargetFrameworks.Add(new ProjectRestoreMetadataFrameworkInfo(framework) | ||
| { | ||
| TargetAlias = tfmShort | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| { | ||
| "profiles": { | ||
| "Aspire.Managed": { | ||
| "commandName": "Project", | ||
| "launchBrowser": true, | ||
| "environmentVariables": { | ||
| "ASPNETCORE_ENVIRONMENT": "Development" | ||
| }, | ||
| "applicationUrl": "https://localhost:50500;http://localhost:50501" | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net10.0</TargetFramework> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <Compile Include="$(TestsSharedDir)TempDirectory.cs" Link="shared/TempDirectory.cs" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\src\Aspire.Managed\Aspire.Managed.csproj" /> | ||
|
|
||
| <PackageReference Include="Microsoft.DotNet.RemoteExecutor" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
Uh oh!
There was an error while loading. Please reload this page.