Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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: 8 additions & 8 deletions src/Aspire.Cli/Backchannel/AppHostBackchannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ namespace Aspire.Cli.Backchannel;

internal sealed class AppHostBackchannel(ILogger<AppHostBackchannel> logger, CliRpcTarget target)
{
private readonly ActivitySource _activitySource = new(nameof(Aspire.Cli.Backchannel.AppHostBackchannel), "1.0.0");
private readonly ActivitySource _activitySource = new(nameof(AppHostBackchannel));
private readonly TaskCompletionSource<JsonRpc> _rpcTaskCompletionSource = new();
private Process? _process;

public async Task<long> PingAsync(long timestamp, CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity(nameof(PingAsync), ActivityKind.Client);
using var activity = _activitySource.StartActivity();

var rpc = await _rpcTaskCompletionSource.Task;

Expand All @@ -37,7 +37,7 @@ public async Task RequestStopAsync(CancellationToken cancellationToken)
// of the AppHost process. The AppHost process will then trigger the shutdown
// which will allow the CLI to await the pending run.

using var activity = _activitySource.StartActivity(nameof(RequestStopAsync), ActivityKind.Client);
using var activity = _activitySource.StartActivity();

var rpc = await _rpcTaskCompletionSource.Task;

Expand All @@ -51,7 +51,7 @@ await rpc.InvokeWithCancellationAsync(

public async Task<(string BaseUrlWithLoginToken, string? CodespacesUrlWithLoginToken)> GetDashboardUrlsAsync(CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity(nameof(GetDashboardUrlsAsync), ActivityKind.Client);
using var activity = _activitySource.StartActivity();

var rpc = await _rpcTaskCompletionSource.Task;

Expand All @@ -67,7 +67,7 @@ await rpc.InvokeWithCancellationAsync(

public async IAsyncEnumerable<(string Resource, string Type, string State, string[] Endpoints)> GetResourceStatesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity(nameof(GetResourceStatesAsync), ActivityKind.Client);
using var activity = _activitySource.StartActivity();

var rpc = await _rpcTaskCompletionSource.Task;

Expand All @@ -88,7 +88,7 @@ await rpc.InvokeWithCancellationAsync(

public async Task ConnectAsync(Process process, string socketPath, CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity(nameof(ConnectAsync), ActivityKind.Client);
using var activity = _activitySource.StartActivity();

_process = process;

Expand All @@ -111,7 +111,7 @@ public async Task ConnectAsync(Process process, string socketPath, CancellationT

public async Task<string[]> GetPublishersAsync(CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity(nameof(GetPublishersAsync), ActivityKind.Client);
using var activity = _activitySource.StartActivity();

var rpc = await _rpcTaskCompletionSource.Task.ConfigureAwait(false);

Expand All @@ -127,7 +127,7 @@ public async Task<string[]> GetPublishersAsync(CancellationToken cancellationTok

public async IAsyncEnumerable<(string Id, string StatusText, bool IsComplete, bool IsError)> GetPublishingActivitiesAsync([EnumeratorCancellation]CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity(nameof(GetPublishingActivitiesAsync), ActivityKind.Client);
using var activity = _activitySource.StartActivity();

var rpc = await _rpcTaskCompletionSource.Task;

Expand Down
6 changes: 3 additions & 3 deletions src/Aspire.Cli/Commands/AddCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace Aspire.Cli.Commands;

internal sealed class AddCommand : BaseCommand
{
private readonly ActivitySource _activitySource = new ActivitySource("Aspire.Cli");
private readonly ActivitySource _activitySource = new ActivitySource(nameof(AddCommand));
private readonly DotNetCliRunner _runner;
private readonly INuGetPackageCache _nuGetPackageCache;

Expand Down Expand Up @@ -42,7 +42,7 @@ public AddCommand(DotNetCliRunner runner, INuGetPackageCache nuGetPackageCache)

protected override async Task<int> ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity($"{nameof(ExecuteAsync)}", ActivityKind.Internal);
using var activity = _activitySource.StartActivity();

try
{
Expand All @@ -62,7 +62,7 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell

var packages = await AnsiConsole.Status().StartAsync(
"Searching for Aspire packages...",
context => _nuGetPackageCache.GetPackagesAsync(effectiveAppHostProjectFile, prerelease, source, cancellationToken)
context => _nuGetPackageCache.GetPackagesAsync(effectiveAppHostProjectFile.Directory!, prerelease, source, cancellationToken)
);

var version = parseResult.GetValue<string?>("--version");
Expand Down
142 changes: 80 additions & 62 deletions src/Aspire.Cli/Commands/NewCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,27 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.CommandLine;
using System.CommandLine.Parsing;
using System.Diagnostics;
using Aspire.Cli.Utils;
using Semver;
using Spectre.Console;

namespace Aspire.Cli.Commands;

internal sealed class NewCommand : BaseCommand
{
private readonly ActivitySource _activitySource = new ActivitySource("Aspire.Cli");
private readonly ActivitySource _activitySource = new ActivitySource(nameof(NewCommand));
private readonly DotNetCliRunner _runner;
private readonly INuGetPackageCache _nuGetPackageCache;

public NewCommand(DotNetCliRunner runner) : base("new", "Create a new Aspire sample project.")
public NewCommand(DotNetCliRunner runner, INuGetPackageCache nuGetPackageCache) : base("new", "Create a new Aspire sample project.")
{
ArgumentNullException.ThrowIfNull(runner, nameof(runner));
ArgumentNullException.ThrowIfNull(nuGetPackageCache, nameof(nuGetPackageCache));
_runner = runner;
_nuGetPackageCache = nuGetPackageCache;

var templateArgument = new Argument<string>("template");
templateArgument.Validators.Add(ValidateProjectTemplate);
templateArgument.Arity = ArgumentArity.ZeroOrOne;
Arguments.Add(templateArgument);

Expand All @@ -29,9 +31,6 @@ internal sealed class NewCommand : BaseCommand

var outputOption = new Option<string?>("--output", "-o");
Options.Add(outputOption);

var prereleaseOption = new Option<bool>("--prerelease");
Options.Add(prereleaseOption);

var sourceOption = new Option<string?>("--source", "-s");
Options.Add(sourceOption);
Expand All @@ -40,7 +39,7 @@ internal sealed class NewCommand : BaseCommand
Options.Add(templateVersionOption);
}

private static void ValidateProjectTemplate(ArgumentResult result)
private static async Task<(string TemplateName, string TemplateDescription, string? PathAppendage)> GetProjectTemplateAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
// TODO: We need to integrate with the template engine to interrogate
// the list of available templates. For now we will just hard-code
Expand All @@ -49,55 +48,91 @@ private static void ValidateProjectTemplate(ArgumentResult result)
// Once we integrate with template engine we will also be able to
// interrogate the various options and add them. For now we will
// keep it simple.
string[] validTemplates = [
"aspire-starter",
"aspire",
"aspire-apphost",
"aspire-servicedefaults",
"aspire-mstest",
"aspire-nunit",
"aspire-xunit"
(string TemplateName, string TemplateDescription, string? PathAppendage)[] validTemplates = [
("aspire-starter", "Aspire Starter App", "src") ,
("aspire", "Aspire Empty App", "src"),
("aspire-apphost", "Aspire App Host", null),
("aspire-servicedefaults", "Aspire Service Defaults", null),
("aspire-mstest", "Aspire Test Project (MSTest)", null),
("aspire-nunit", "Aspire Test Project (NUnit)", null),
("aspire-xunit", "Aspire Test Project (xUnit)", null)
];

var value = result.GetValueOrDefault<string>();

if (value is null)
if (parseResult.GetValue<string?>("template") is { } templateName && validTemplates.SingleOrDefault(t => t.TemplateName == templateName) is { } template)
{
// This is OK, for now we will use the default
// template of aspire-starter, but we might
// be able to do more intelligent selection in the
// future based on what is already in the working directory.
return;
return template;
}

if (value is { } templateName && !validTemplates.Contains(templateName))
else
{
result.AddError($"The specified template '{templateName}' is not valid. Valid templates are [{string.Join(", ", validTemplates)}].");
return;
return await PromptUtils.PromptForSelectionAsync(
"Select a project template:",
validTemplates,
t => $"{t.TemplateName} ({t.TemplateDescription})",
cancellationToken
);
}
}

protected override async Task<int> ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken)
private static async Task<string> GetProjectNameAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity($"{nameof(ExecuteAsync)}", ActivityKind.Internal);
if (parseResult.GetValue<string>("--name") is not { } name)
{
var defaultName = new DirectoryInfo(Environment.CurrentDirectory).Name;
name = await PromptUtils.PromptForStringAsync("Enter the project name:",
defaultValue: defaultName,
cancellationToken: cancellationToken);
}

var templateVersion = parseResult.GetValue<string>("--version");
var prerelease = parseResult.GetValue<bool>("--prerelease");
return name;
}

if (templateVersion is not null && prerelease)
private static async Task<string> GetOutputPathAsync(ParseResult parseResult, string? pathAppendage, CancellationToken cancellationToken)
{
if (parseResult.GetValue<string>("--output") is not { } outputPath)
{
AnsiConsole.MarkupLine("[red bold]:thumbs_down: The --version and --prerelease options are mutually exclusive.[/]");
return ExitCodeConstants.FailedToCreateNewProject;
outputPath = await PromptUtils.PromptForStringAsync(
"Enter the output path:",
defaultValue: Path.Combine(Environment.CurrentDirectory, pathAppendage ?? string.Empty),
cancellationToken: cancellationToken
);
}
else if (prerelease)

return Path.GetFullPath(outputPath);
}

private static async Task<string> GetProjectTemplatesVersionAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
if (parseResult.GetValue<string>("--version") is { } version)
{
templateVersion = "*-*";
return version;
}
else if (templateVersion is null)
else
{
templateVersion = VersionHelper.GetDefaultTemplateVersion();
version = await PromptUtils.PromptForStringAsync(
"Project templates version:",
defaultValue: VersionHelper.GetDefaultTemplateVersion(),
validator: (string value) => {
if (SemVersion.TryParse(value, out var parsedVersion))
{
return ValidationResult.Success();
}

return ValidationResult.Error("Invalid version format. Please enter a valid version.");
},
cancellationToken);

return version;
}
}

protected override async Task<int> ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity();

var template = await GetProjectTemplateAsync(parseResult, cancellationToken);
var name = await GetProjectNameAsync(parseResult, cancellationToken);
var outputPath = await GetOutputPathAsync(parseResult, template.PathAppendage, cancellationToken);
var version = await GetProjectTemplatesVersionAsync(parseResult, cancellationToken);
var source = parseResult.GetValue<string?>("--source");

var templateInstallResult = await AnsiConsole.Status()
Expand All @@ -106,7 +141,7 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell
.StartAsync(
":ice: Getting latest templates...",
async context => {
return await _runner.InstallTemplateAsync("Aspire.ProjectTemplates", templateVersion!, source, true, cancellationToken);
return await _runner.InstallTemplateAsync("Aspire.ProjectTemplates", version, source, true, cancellationToken);
});

if (templateInstallResult.ExitCode != 0)
Expand All @@ -117,35 +152,18 @@ protected override async Task<int> ExecuteAsync(ParseResult parseResult, Cancell

AnsiConsole.MarkupLine($":package: Using project templates version: {templateInstallResult.TemplateVersion}");

var templateName = parseResult.GetValue<string>("template") ?? "aspire-starter";

if (parseResult.GetValue<string>("--output") is not { } outputPath)
{
outputPath = Environment.CurrentDirectory;
}
else
{
outputPath = Path.GetFullPath(outputPath);
}

if (parseResult.GetValue<string>("--name") is not { } name)
{
var outputPathDirectoryInfo = new DirectoryInfo(outputPath);
name = outputPathDirectoryInfo.Name;
}

int newProjectExitCode = await AnsiConsole.Status()
.Spinner(Spinner.Known.Dots3)
.SpinnerStyle(Style.Parse("purple"))
.StartAsync(
":rocket: Creating new Aspire project...",
async context => {
return await _runner.NewProjectAsync(
templateName,
name,
outputPath,
cancellationToken);
});
template.TemplateName,
name,
outputPath,
cancellationToken);
});

if (newProjectExitCode != 0)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Cli/Commands/PublishCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Aspire.Cli.Commands;

internal sealed class PublishCommand : BaseCommand
{
private readonly ActivitySource _activitySource = new ActivitySource("Aspire.Cli");
private readonly ActivitySource _activitySource = new ActivitySource(nameof(PublishCommand));
private readonly DotNetCliRunner _runner;

public PublishCommand(DotNetCliRunner runner) : base("publish", "Generates deployment artifacts for an Aspire app host project.")
Expand All @@ -34,7 +34,7 @@ internal sealed class PublishCommand : BaseCommand

protected override async Task<int> ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity($"{nameof(ExecuteAsync)}", ActivityKind.Internal);
using var activity = _activitySource.StartActivity();

var passedAppHostProjectFile = parseResult.GetValue<FileInfo?>("--project");
var effectiveAppHostProjectFile = ProjectFileHelper.UseOrFindAppHostProjectFile(passedAppHostProjectFile);
Expand Down
4 changes: 2 additions & 2 deletions src/Aspire.Cli/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Aspire.Cli.Commands;

internal sealed class RunCommand : BaseCommand
{
private readonly ActivitySource _activitySource = new ActivitySource("Aspire.Cli");
private readonly ActivitySource _activitySource = new ActivitySource(nameof(RunCommand));
private readonly DotNetCliRunner _runner;

public RunCommand(DotNetCliRunner runner) : base("run", "Run an Aspire app host in development mode.")
Expand All @@ -33,7 +33,7 @@ public RunCommand(DotNetCliRunner runner) : base("run", "Run an Aspire app host

protected override async Task<int> ExecuteAsync(ParseResult parseResult, CancellationToken cancellationToken)
{
using var activity = _activitySource.StartActivity($"{nameof(ExecuteAsync)}", ActivityKind.Internal);
using var activity = _activitySource.StartActivity();

var passedAppHostProjectFile = parseResult.GetValue<FileInfo?>("--project");
var effectiveAppHostProjectFile = ProjectFileHelper.UseOrFindAppHostProjectFile(passedAppHostProjectFile);
Expand Down
Loading
Loading