Skip to content
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

Hide default parameters in project resources #7333

Closed
Closed
Changes from 1 commit
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
Next Next commit
Remove DCP default args for project resources
Adam Ratzman committed Jan 30, 2025
commit 50311412fbc8c47a619bb88f760d38b7682cccc6
115 changes: 106 additions & 9 deletions src/Aspire.Dashboard/Model/ResourceSourceViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text;

namespace Aspire.Dashboard.Model;

public class ResourceSourceViewModel(string value, string? contentAfterValue, string valueToVisualize, string tooltip)
@@ -12,31 +14,35 @@ public class ResourceSourceViewModel(string value, string? contentAfterValue, st

internal static ResourceSourceViewModel? GetSourceViewModel(ResourceViewModel resource)
{
var executablePath = resource.TryGetExecutablePath(out var path) ? path : null;

(string? ArgumentsString, string FullCommandLine)? commandLineInfo = null;
string? executablePath;
(string? NonDefaultArguments, string FullCommandLine)? commandLineInfo;

if (resource.TryGetExecutableArguments(out var arguments))
if (resource.TryGetExecutablePath(out var path))
{
executablePath = path;
commandLineInfo = GetCommandLineInfo(resource, executablePath);
}
else
{
var argumentsString = arguments.IsDefaultOrEmpty ? null : string.Join(" ", arguments);
commandLineInfo = (ArgumentsString: argumentsString, $"{executablePath} {argumentsString}");
executablePath = null;
commandLineInfo = null;
}

// NOTE projects are also executables, so we have to check for projects first
if (resource.IsProject() && resource.TryGetProjectPath(out var projectPath))
{
if (commandLineInfo is { ArgumentsString: { } argumentsString, FullCommandLine: { } fullCommandLine })
if (commandLineInfo is { NonDefaultArguments: { } argumentsString, FullCommandLine: { } fullCommandLine })
{
return new ResourceSourceViewModel(value: Path.GetFileName(projectPath), contentAfterValue: argumentsString, valueToVisualize: fullCommandLine, tooltip: fullCommandLine);
}

// default to project path if there is no executable path or executable arguments
return new ResourceSourceViewModel(value: Path.GetFileName(projectPath), contentAfterValue: commandLineInfo?.ArgumentsString, valueToVisualize: projectPath, tooltip: projectPath);
return new ResourceSourceViewModel(value: Path.GetFileName(projectPath), contentAfterValue: commandLineInfo?.NonDefaultArguments, valueToVisualize: projectPath, tooltip: projectPath);
}

if (executablePath is not null)
{
return new ResourceSourceViewModel(value: Path.GetFileName(executablePath), contentAfterValue: commandLineInfo?.ArgumentsString, valueToVisualize: commandLineInfo?.FullCommandLine ?? executablePath, tooltip: commandLineInfo?.FullCommandLine ?? string.Empty);
return new ResourceSourceViewModel(value: Path.GetFileName(executablePath), contentAfterValue: commandLineInfo?.NonDefaultArguments, valueToVisualize: commandLineInfo?.FullCommandLine ?? executablePath, tooltip: commandLineInfo?.FullCommandLine ?? string.Empty);
}

if (resource.TryGetContainerImage(out var containerImage))
@@ -51,4 +57,95 @@ public class ResourceSourceViewModel(string value, string? contentAfterValue, st

return null;
}

/**
* Returns information about command line arguments, stripping out DCP default arguments, if any exist.
* The defaults come from DcpExecutor#PrepareProjectExecutables and need to be kept in sync
*/
private static (string? NonDefaultArguments, string FullCommandLine)? GetCommandLineInfo(ResourceViewModel resource, string executablePath)
{
if (resource.TryGetExecutableArguments(out var arguments))
{
if (arguments.IsDefaultOrEmpty)
{
return (NonDefaultArguments: null, FullCommandLine: executablePath);
}

var escapedArguments = arguments.Select(EscapeCommandLineArgument).ToList();

if (resource.IsProject())
{
if (escapedArguments.Count > 3 && escapedArguments.Take(3).SequenceEqual(["run", "--no-build", "--project"], StringComparers.CommandLineArguments))
{
escapedArguments.RemoveRange(0, 4); // remove the project path too
}
else if (escapedArguments.Count > 4 && escapedArguments.Take(4).SequenceEqual(["watch", "--non-interactive", "--no-hot-reload", "--project"], StringComparers.CommandLineArguments))
{
escapedArguments.RemoveRange(0, 5); // remove the project path too
}

if (escapedArguments.Count > 1 && string.Equals(escapedArguments[0], "-c", StringComparisons.CommandLineArguments))
{
escapedArguments.RemoveRange(0, 2);
}

if (escapedArguments.Count > 0 && string.Equals(escapedArguments[0], "--no-launch-profile", StringComparisons.CommandLineArguments))
{
escapedArguments.RemoveAt(0);
}
}

var cleanedArguments = escapedArguments.Count == 0 ? null : string.Join(' ', escapedArguments);
var fullCommandLine = resource.TryGetProjectPath(out var projectPath)
? AppendArgumentsIfNotEmpty(projectPath, cleanedArguments)
: AppendArgumentsIfNotEmpty(executablePath, cleanedArguments);

return (NonDefaultArguments: cleanedArguments ?? string.Empty, FullCommandLine: fullCommandLine);

static string AppendArgumentsIfNotEmpty(string s, string? arguments) => arguments is null ? s : $"{s} {arguments}";
}

return null;

// This method doesn't account for all cases, but does the most common
static string EscapeCommandLineArgument(string argument)
{
if (string.IsNullOrEmpty(argument))
{
return "\"\"";
}

if (argument.Contains(' ') || argument.Contains('"') || argument.Contains('\\'))
{
var escapedArgument = new StringBuilder();
escapedArgument.Append('"');

for (int i = 0; i < argument.Length; i++)
{
char c = argument[i];
switch (c)
{
case '\\':
// Escape backslashes
escapedArgument.Append('\\');
escapedArgument.Append('\\');
break;
case '"':
// Escape quotes
escapedArgument.Append('\\');
escapedArgument.Append('"');
break;
default:
escapedArgument.Append(c);
break;
}
}

escapedArgument.Append('"');
return escapedArgument.ToString();
}

return argument;
}
}
}
2 changes: 2 additions & 0 deletions src/Shared/StringComparers.cs
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ internal static class StringComparers
public static StringComparer OtlpSpanId => StringComparer.Ordinal;
public static StringComparer HealthReportPropertyValue => StringComparer.Ordinal;
public static StringComparer CultureName => StringComparer.OrdinalIgnoreCase;
public static StringComparer CommandLineArguments => StringComparer.Ordinal;
}

internal static class StringComparisons
@@ -49,4 +50,5 @@ internal static class StringComparisons
public static StringComparison OtlpSpanId => StringComparison.Ordinal;
public static StringComparison HealthReportPropertyValue => StringComparison.Ordinal;
public static StringComparison CultureName => StringComparison.OrdinalIgnoreCase;
public static StringComparison CommandLineArguments => StringComparison.Ordinal;
}
Original file line number Diff line number Diff line change
@@ -65,8 +65,8 @@ void AddStringProperty(string propertyName, string? propertyValue)
new ResourceSourceViewModel(
value: "project",
contentAfterValue: "arg1 arg2",
valueToVisualize: "path/to/executable arg1 arg2",
tooltip: "path/to/executable arg1 arg2"));
valueToVisualize: "path/to/project arg1 arg2",
tooltip: "path/to/project arg1 arg2"));

// Project without executable arguments
data.Add(new TestData(