Skip to content

Commit 1994993

Browse files
authored
Order package versions in CLI. (#8889)
* Fix sort order of packages. * Fix sorting issue in aspire new.
1 parent 62c6cf0 commit 1994993

File tree

4 files changed

+294
-2
lines changed

4 files changed

+294
-2
lines changed

src/Aspire.Cli/Commands/AddCommand.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Text;
77
using Aspire.Cli.Interaction;
88
using Aspire.Cli.Projects;
9+
using Semver;
910
using Spectre.Console;
1011

1112
namespace Aspire.Cli.Commands;
@@ -190,7 +191,8 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell
190191
}
191192

192193
// ... otherwise we had better prompt.
193-
var version = await _prompter.PromptForIntegrationVersionAsync(packageVersions, cancellationToken);
194+
var orderedPackageVersions = packageVersions.OrderByDescending(p => SemVersion.Parse(p.Package.Version), SemVersion.PrecedenceComparer);
195+
var version = await _prompter.PromptForIntegrationVersionAsync(orderedPackageVersions, cancellationToken);
194196

195197
return version;
196198
}

src/Aspire.Cli/Commands/NewCommand.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Diagnostics;
66
using Aspire.Cli.Certificates;
77
using Aspire.Cli.Interaction;
8+
using Semver;
89
namespace Aspire.Cli.Commands;
910

1011
internal sealed class NewCommand : BaseCommand
@@ -122,7 +123,8 @@ private async Task<string> GetProjectTemplatesVersionAsync(ParseResult parseResu
122123
() => _nuGetPackageCache.GetTemplatePackagesAsync(workingDirectory, prerelease, source, cancellationToken)
123124
);
124125

125-
var selectedPackage = await _prompter.PromptForTemplatesVersionAsync(candidatePackages, cancellationToken);
126+
var orderedCandidatePackages = candidatePackages.OrderByDescending(p => SemVersion.Parse(p.Version), SemVersion.PrecedenceComparer);
127+
var selectedPackage = await _prompter.PromptForTemplatesVersionAsync(orderedCandidatePackages, cancellationToken);
126128
return selectedPackage.Version;
127129
}
128130
}

tests/Aspire.Cli.Tests/Commands/AddCommandTests.cs

+150
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,156 @@ public async Task AddCommandInteractiveFlowSmokeTest()
8888
Assert.Equal(0, exitCode);
8989
}
9090

91+
[Fact]
92+
public async Task AddCommandSortsPackageVersions()
93+
{
94+
IEnumerable<(string FriendlyName, NuGetPackage Package)>? promptedPackages = null;
95+
96+
var services = CliTestHelper.CreateServiceCollection(outputHelper, options => {
97+
98+
options.AddCommandPrompterFactory = (sp) =>
99+
{
100+
var interactionService = sp.GetRequiredService<IInteractionService>();
101+
var prompter = new TestAddCommandPrompter(interactionService);
102+
103+
prompter.PromptForIntegrationVersionCallback = (packages) =>
104+
{
105+
promptedPackages = packages;
106+
return packages.First();
107+
};
108+
109+
return prompter;
110+
};
111+
112+
options.ProjectLocatorFactory = _ => new TestProjectLocator();
113+
114+
options.DotNetCliRunnerFactory = (sp) =>
115+
{
116+
var runner = new TestDotNetCliRunner();
117+
runner.SearchPackagesAsyncCallback = (dir, query, prerelease, take, skip, nugetSource, cancellationToken) =>
118+
{
119+
var redis92Package = new NuGetPackage()
120+
{
121+
Id = "Aspire.Hosting.Redis",
122+
Source = "nuget",
123+
Version = "9.2.0"
124+
};
125+
126+
var redis93Package = new NuGetPackage()
127+
{
128+
Id = "Aspire.Hosting.Redis",
129+
Source = "nuget",
130+
Version = "9.3.0"
131+
};
132+
133+
return (
134+
0, // Exit code.
135+
new NuGetPackage[] { redis92Package, redis93Package } //
136+
);
137+
};
138+
139+
runner.AddPackageAsyncCallback = (projectFilePath, packageName, packageVersion, cancellationToken) =>
140+
{
141+
// Simulate adding the package.
142+
return 0; // Success.
143+
};
144+
145+
return runner;
146+
};
147+
});
148+
var provider = services.BuildServiceProvider();
149+
150+
var command = provider.GetRequiredService<AddCommand>();
151+
var result = command.Parse("add");
152+
153+
var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
154+
Assert.Equal(0, exitCode);
155+
Assert.Collection(
156+
promptedPackages!,
157+
p => Assert.Equal("9.3.0", p.Package.Version),
158+
p => Assert.Equal("9.2.0", p.Package.Version)
159+
);
160+
}
161+
162+
[Fact]
163+
public async Task AddCommandSortsPackageVersionsWithPrerelease()
164+
{
165+
IEnumerable<(string FriendlyName, NuGetPackage Package)>? promptedPackages = null;
166+
167+
var services = CliTestHelper.CreateServiceCollection(outputHelper, options => {
168+
169+
options.AddCommandPrompterFactory = (sp) =>
170+
{
171+
var interactionService = sp.GetRequiredService<IInteractionService>();
172+
var prompter = new TestAddCommandPrompter(interactionService);
173+
174+
prompter.PromptForIntegrationVersionCallback = (packages) =>
175+
{
176+
promptedPackages = packages;
177+
return packages.First();
178+
};
179+
180+
return prompter;
181+
};
182+
183+
options.ProjectLocatorFactory = _ => new TestProjectLocator();
184+
185+
options.DotNetCliRunnerFactory = (sp) =>
186+
{
187+
var runner = new TestDotNetCliRunner();
188+
runner.SearchPackagesAsyncCallback = (dir, query, prerelease, take, skip, nugetSource, cancellationToken) =>
189+
{
190+
var redis92Package = new NuGetPackage()
191+
{
192+
Id = "Aspire.Hosting.Redis",
193+
Source = "nuget",
194+
Version = "9.2.0"
195+
};
196+
197+
var redis94PrereleasePackage = new NuGetPackage()
198+
{
199+
Id = "Aspire.Hosting.Redis",
200+
Source = "nuget",
201+
Version = "9.4.0-preview1.1234"
202+
};
203+
204+
var redis93Package = new NuGetPackage()
205+
{
206+
Id = "Aspire.Hosting.Redis",
207+
Source = "nuget",
208+
Version = "9.3.0"
209+
};
210+
211+
return (
212+
0, // Exit code.
213+
new NuGetPackage[] { redis92Package, redis94PrereleasePackage, redis93Package } //
214+
);
215+
};
216+
217+
runner.AddPackageAsyncCallback = (projectFilePath, packageName, packageVersion, cancellationToken) =>
218+
{
219+
// Simulate adding the package.
220+
return 0; // Success.
221+
};
222+
223+
return runner;
224+
};
225+
});
226+
var provider = services.BuildServiceProvider();
227+
228+
var command = provider.GetRequiredService<AddCommand>();
229+
var result = command.Parse("add");
230+
231+
var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
232+
Assert.Equal(0, exitCode);
233+
Assert.Collection(
234+
promptedPackages!,
235+
p => Assert.Equal("9.4.0-preview1.1234", p.Package.Version),
236+
p => Assert.Equal("9.3.0", p.Package.Version),
237+
p => Assert.Equal("9.2.0", p.Package.Version)
238+
);
239+
}
240+
91241
[Fact]
92242
public async Task AddCommandDoesNotPromptForIntegrationArgumentIfSpecifiedOnCommandLine()
93243
{

tests/Aspire.Cli.Tests/Commands/NewCommandTests.cs

+138
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,144 @@ public async Task NewCommandInteractiveFlowSmokeTest()
6767
Assert.Equal(0, exitCode);
6868
}
6969

70+
[Fact]
71+
public async Task NewCommandOrdersTemplatePackageVersionsCorrectly()
72+
{
73+
IEnumerable<NuGetPackage>? promptedPackages = null;
74+
75+
var services = CliTestHelper.CreateServiceCollection(outputHelper, options => {
76+
77+
// Set of options that we'll give when prompted.
78+
options.NewCommandPrompterFactory = (sp) =>
79+
{
80+
var interactionService = sp.GetRequiredService<IInteractionService>();
81+
var prompter = new TestNewCommandPrompter(interactionService);
82+
83+
prompter.PromptForTemplatesVersionCallback = (packages) =>
84+
{
85+
promptedPackages = packages;
86+
return promptedPackages.First();
87+
};
88+
89+
return prompter;
90+
};
91+
92+
options.DotNetCliRunnerFactory = (sp) =>
93+
{
94+
var runner = new TestDotNetCliRunner();
95+
runner.SearchPackagesAsyncCallback = (dir, query, prerelease, take, skip, nugetSource, cancellationToken) =>
96+
{
97+
var package92 = new NuGetPackage()
98+
{
99+
Id = "Aspire.ProjectTemplates",
100+
Source = "othernuget",
101+
Version = "9.2.0"
102+
};
103+
104+
var package93 = new NuGetPackage()
105+
{
106+
Id = "Aspire.ProjectTemplates",
107+
Source = "nuget",
108+
Version = "9.3.0"
109+
};
110+
111+
return (
112+
0, // Exit code.
113+
new NuGetPackage[] { package92, package93 }
114+
);
115+
};
116+
117+
return runner;
118+
};
119+
});
120+
var provider = services.BuildServiceProvider();
121+
122+
var command = provider.GetRequiredService<NewCommand>();
123+
var result = command.Parse("new");
124+
125+
var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
126+
Assert.Equal(0, exitCode);
127+
Assert.NotNull(promptedPackages);
128+
Assert.Collection(
129+
promptedPackages,
130+
package => Assert.Equal("9.3.0", package.Version),
131+
package => Assert.Equal("9.2.0", package.Version)
132+
);
133+
}
134+
135+
[Fact]
136+
public async Task NewCommandOrdersTemplatePackageVersionsCorrectlyWithPrerelease()
137+
{
138+
IEnumerable<NuGetPackage>? promptedPackages = null;
139+
140+
var services = CliTestHelper.CreateServiceCollection(outputHelper, options => {
141+
142+
// Set of options that we'll give when prompted.
143+
options.NewCommandPrompterFactory = (sp) =>
144+
{
145+
var interactionService = sp.GetRequiredService<IInteractionService>();
146+
var prompter = new TestNewCommandPrompter(interactionService);
147+
148+
prompter.PromptForTemplatesVersionCallback = (packages) =>
149+
{
150+
promptedPackages = packages;
151+
return promptedPackages.First();
152+
};
153+
154+
return prompter;
155+
};
156+
157+
options.DotNetCliRunnerFactory = (sp) =>
158+
{
159+
var runner = new TestDotNetCliRunner();
160+
runner.SearchPackagesAsyncCallback = (dir, query, prerelease, take, skip, nugetSource, cancellationToken) =>
161+
{
162+
var package92 = new NuGetPackage()
163+
{
164+
Id = "Aspire.ProjectTemplates",
165+
Source = "othernuget",
166+
Version = "9.2.0"
167+
};
168+
169+
var package94 = new NuGetPackage()
170+
{
171+
Id = "Aspire.ProjectTemplates",
172+
Source = "internalfeed",
173+
Version = "9.4.0-preview.1234"
174+
};
175+
176+
var package93 = new NuGetPackage()
177+
{
178+
Id = "Aspire.ProjectTemplates",
179+
Source = "nuget",
180+
Version = "9.3.0"
181+
};
182+
183+
return (
184+
0, // Exit code.
185+
new NuGetPackage[] { package92, package94, package93 }
186+
);
187+
};
188+
189+
return runner;
190+
};
191+
});
192+
var provider = services.BuildServiceProvider();
193+
194+
var command = provider.GetRequiredService<NewCommand>();
195+
var result = command.Parse("new");
196+
197+
var exitCode = await result.InvokeAsync().WaitAsync(CliTestConstants.DefaultTimeout);
198+
Assert.Equal(0, exitCode);
199+
Assert.NotNull(promptedPackages);
200+
Assert.Collection(
201+
promptedPackages,
202+
package => Assert.Equal("9.4.0-preview.1234", package.Version),
203+
package => Assert.Equal("9.3.0", package.Version),
204+
package => Assert.Equal("9.2.0", package.Version)
205+
);
206+
}
207+
70208
[Fact]
71209
public async Task NewCommandDoesNotPromptForProjectNameIfSpecifiedOnCommandLine()
72210
{

0 commit comments

Comments
 (0)