Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
7 changes: 5 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<NoWarn>$(NoWarn);NU1507</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Azure.Monitor.OpenTelemetry.Exporter" Version="1.4.0" />
<PackageVersion Include="Basic.CompilerLog.Util" Version="0.9.9" />
<PackageVersion Include="AwesomeAssertions" Version="$(AwesomeAssertionsVersion)" />
<PackageVersion Include="BenchmarkDotNet" Version="$(BenchmarkDotNetPackageVersion)" />
Expand Down Expand Up @@ -110,6 +111,9 @@
<PackageVersion Include="NuGet.ProjectModel" Version="$(NuGetProjectModelPackageVersion)" />
<PackageVersion Include="NuGet.Protocol" Version="$(NuGetBuildTasksPackageVersion)" />
<PackageVersion Include="NuGet.Versioning" Version="$(NuGetVersioningPackageVersion)" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
<PackageVersion Include="runtime.linux-musl-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion)" />
<PackageVersion Include="runtime.linux-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion)" />
<PackageVersion Include="runtime.osx-x64.Microsoft.NETCore.DotNetHostResolver" Version="$(MicrosoftNETCoreDotNetHostResolverPackageVersion)" />
Expand Down Expand Up @@ -144,7 +148,6 @@
<PackageVersion Include="xunit.assert" Version="$(XUnitVersion)" Condition="'$(IsTestProject)' != 'true'" />
<PackageVersion Include="xunit.console" Version="$(XUnitVersion)" />
</ItemGroup>

<!-- Use different versions of Microsoft.Build.* depending on whether the output will be used in
.NET Framework (VS) or only in the .NET SDK.

Expand Down Expand Up @@ -176,4 +179,4 @@
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="$(MicrosoftBuildMinimumVersion)" />
<PackageVersion Include="Microsoft.NET.StringTools" Version="$(MicrosoftBuildMinimumVersion)" />
</ItemGroup>
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private static string GetMSBuildExePath()
MSBuildExeName);
}

private static string GetMSBuildSDKsPath()
public static string GetMSBuildSDKsPath()
{
var envMSBuildSDKsPath = Environment.GetEnvironmentVariable("MSBuildSDKsPath");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,8 @@
<PackageReference Include="Microsoft.Build" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="Microsoft.Build.Utilities.Core" ExcludeAssets="runtime" PrivateAssets="all" />
<PackageReference Include="System.CommandLine" />
<PackageReference Include="System.IO.Hashing" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<PackageReference Include="System.Diagnostics.DiagnosticSource" />
<PackageReference Include="System.IO.Hashing" />
</ItemGroup>

<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
Expand Down
18 changes: 9 additions & 9 deletions src/Cli/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static class TelemetryEventEntry
public static ITelemetryFilter TelemetryFilter { get; set; } = new BlockFilter();

public static void TrackEvent(
string? eventName = null,
string eventName,
Copy link

Copilot AI Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter 'eventName' is now non-nullable (removed the ?), but the method body still checks for null at line 18 with if (string.IsNullOrEmpty(eventName)). Either the parameter should remain nullable with string?, or the null check should be removed since non-nullable parameters cannot be null when nullable reference types are enabled.

Copilot uses AI. Check for mistakes.
IDictionary<string, string?>? properties = null,
IDictionary<string, double>? measurements = null)
{
Expand All @@ -32,7 +32,7 @@ public static void SendFiltered(object? o = null)
}
}

public static void Subscribe(Action<string?, IDictionary<string, string?>?, IDictionary<string, double>?> subscriber)
public static void Subscribe(Action<string, IDictionary<string, string?>?, IDictionary<string, double>?> subscriber)
{
void Handler(object? sender, InstrumentationEventArgs eventArgs)
{
Expand All @@ -47,24 +47,24 @@ public sealed class PerformanceMeasurement : IDisposable
{
private readonly Stopwatch? _timer;
private readonly Dictionary<string, double>? _data;
private readonly string? _name;
private readonly string _name;

public PerformanceMeasurement(Dictionary<string, double>? data, string name)
{
_name = name;
// Measurement is a no-op if we don't have a dictionary to store the entry.
if (data == null)
{
return;
}

_data = data;
_name = name;
_timer = Stopwatch.StartNew();
}

public void Dispose()
{
if (_name is not null && _timer is not null)
if (_timer is not null)
{
_data?.Add(_name, _timer.Elapsed.TotalMilliseconds);
}
Expand All @@ -82,7 +82,7 @@ public IEnumerable<ApplicationInsightsEntryFormat> Filter(object o)
public class InstrumentationEventArgs : EventArgs
{
internal InstrumentationEventArgs(
string? eventName,
string eventName,
IDictionary<string, string?>? properties,
IDictionary<string, double>? measurements)
{
Expand All @@ -91,17 +91,17 @@ internal InstrumentationEventArgs(
Measurements = measurements;
}

public string? EventName { get; }
public string EventName { get; }
public IDictionary<string, string?>? Properties { get; }
public IDictionary<string, double>? Measurements { get; }
}

public class ApplicationInsightsEntryFormat(
string? eventName = null,
string eventName,
IDictionary<string, string?>? properties = null,
IDictionary<string, double>? measurements = null)
{
public string? EventName { get; } = eventName;
public string EventName { get; } = eventName;
public IDictionary<string, string?>? Properties { get; } = properties;
public IDictionary<string, double>? Measurements { get; } = measurements;

Expand Down
45 changes: 22 additions & 23 deletions src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs
Original file line number Diff line number Diff line change
@@ -1,34 +1,33 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.Extensions.EnvironmentAbstractions
namespace Microsoft.Extensions.EnvironmentAbstractions;

public readonly struct FilePath
{
public struct FilePath
{
public string Value { get; }
public string Value { get; }

/// <summary>
/// Create FilePath to represent an absolute file path. Note it may not exist.
/// </summary>
/// <param name="value">If the value is not rooted. Path.GetFullPath will be called during the constructor.</param>
public FilePath(string value)
/// <summary>
/// Create FilePath to represent an absolute file path. Note: It may not exist.
/// </summary>
/// <param name="value">If the value is not rooted. Path.GetFullPath will be called during the constructor.</param>
public FilePath(string value)
{
if (!Path.IsPathRooted(value))
{
if (!Path.IsPathRooted(value))
{
value = Path.GetFullPath(value);
}

Value = value;
value = Path.GetFullPath(value);
}

public override string ToString()
{
return Value;
}
Value = value;
}

public DirectoryPath GetDirectoryPath()
{
return new DirectoryPath(Path.GetDirectoryName(Value)!);
}
public override string ToString()
{
return Value;
}

public DirectoryPath GetDirectoryPath()
{
return new DirectoryPath(Path.GetDirectoryName(Value)!);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,9 @@ internal async Task<NewCommandStatus> InvokeAsync(ParseResult parseResult, Cance
CancellationTokenSource cancellationTokenSource = new();
cancellationTokenSource.CancelAfter(ConstraintEvaluationTimeout);

#pragma warning disable CA2025 // Do not pass 'IDisposable' instances into unawaited tasks
Task<IReadOnlyList<TemplateConstraintResult>> constraintsEvaluation = ValidateConstraintsAsync(constraintManager, args.Template, args.IsForceFlagSpecified ? cancellationTokenSource.Token : cancellationToken);
#pragma warning restore CA2025 // Do not pass 'IDisposable' instances into unawaited tasks

if (!args.IsForceFlagSpecified)
{
Expand Down
24 changes: 18 additions & 6 deletions src/Cli/dotnet/CliSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,20 @@ internal static class CliSchema
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};

public record ArgumentDetails(string? description, int order, bool hidden, string? helpName, string valueType, bool hasDefaultValue, object? defaultValue, ArityDetails arity);
public record ArityDetails(int minimum, int? maximum);
public record ArgumentDetails(
string? description,
int order,
bool hidden,
string? helpName,
string valueType,
bool hasDefaultValue,
object? defaultValue,
ArityDetails arity);

public record ArityDetails(
int minimum,
int? maximum);

public record OptionDetails(
string? description,
bool hidden,
Expand All @@ -45,15 +57,16 @@ public record OptionDetails(
object? defaultValue,
ArityDetails arity,
bool required,
bool recursive
);
bool recursive);

public record CommandDetails(
string? description,
bool hidden,
string[]? aliases,
Dictionary<string, ArgumentDetails>? arguments,
Dictionary<string, OptionDetails>? options,
Dictionary<string, CommandDetails>? subcommands);

public record RootCommandDetails(
string name,
string version,
Expand All @@ -65,7 +78,6 @@ public record RootCommandDetails(
Dictionary<string, CommandDetails>? subcommands
) : CommandDetails(description, hidden, aliases, arguments, options, subcommands);


public static void PrintCliSchema(CommandResult commandResult, TextWriter outputWriter, ITelemetry? telemetryClient)
{
var command = commandResult.Command;
Expand All @@ -74,7 +86,7 @@ public static void PrintCliSchema(CommandResult commandResult, TextWriter output
outputWriter.Write(result.AsSpan());
outputWriter.Flush();
var commandString = CommandHierarchyAsString(commandResult);
var telemetryProperties = new Dictionary<string, string> { { "command", commandString } };
var telemetryProperties = new Dictionary<string, string?> { { "command", commandString } };
telemetryClient?.TrackEvent("schema", telemetryProperties, null);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Cli/dotnet/CommandDirectoryContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static void PerformActionWithBasePath(string basePath, Action action)
$"Calls to {nameof(CommandDirectoryContext)}.{nameof(PerformActionWithBasePath)} cannot be nested.");
}
_basePath = basePath;
Telemetry.Telemetry.CurrentSessionId = null;
Telemetry.Telemetry.s_currentSessionId = null;
try
{
action();
Expand Down
42 changes: 42 additions & 0 deletions src/Cli/dotnet/CommandFactory/CommandResolution/ActivityContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@

using System.Diagnostics;
using Microsoft.DotNet.Cli.Utils;
using OpenTelemetry;
using OpenTelemetry.Context.Propagation;

namespace Microsoft.DotNet.Cli.CommandFactory.CommandResolution;

public static class ActivityContext
{
public static Dictionary<string, string>? MakeActivityContextEnvironment()
{
var currentActivity = Activity.Current;
var currentBaggage = Baggage.Current;
if (currentActivity == null)
{
return null;
}
var contextToInject = currentActivity.Context;
if (contextToInject.TraceId == default && contextToInject.SpanId == default && contextToInject.TraceState is null)
{
return null;
}
var propagationContext = new PropagationContext(contextToInject, currentBaggage);
var envDict = new Dictionary<string, string>(capacity: 2);
Propagators.DefaultTextMapPropagator.Inject(propagationContext, envDict, WriteTraceStateIntoEnv);
return envDict;
}

private static void WriteTraceStateIntoEnv(Dictionary<string, string> dictionary, string key, string value)
{
switch (key)
{
case "traceparent":
dictionary[Activities.TRACEPARENT] = value;
break;
case "tracestate":
dictionary[Activities.TRACESTATE] = value;
break;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,22 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;

namespace Microsoft.DotNet.Cli.CommandFactory.CommandResolution;

public class MuxerCommandResolver : ICommandResolver
{
public CommandSpec Resolve(CommandResolverArguments commandResolverArguments)
public CommandSpec? Resolve(CommandResolverArguments commandResolverArguments)
{
if (commandResolverArguments.CommandName == Muxer.MuxerName)
{
var muxer = new Muxer();
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(
commandResolverArguments.CommandArguments.OrEmptyIfNull());
return new CommandSpec(muxer.MuxerPath, escapedArgs);
var env = ActivityContext.MakeActivityContextEnvironment();
return new CommandSpec(muxer.MuxerPath, escapedArgs, env);
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.DotNet.Cli.Utils;

namespace Microsoft.DotNet.Cli.CommandFactory.CommandResolution;
Expand All @@ -11,7 +9,8 @@ internal static class MuxerCommandSpecMaker
{
internal static CommandSpec CreatePackageCommandSpecUsingMuxer(
string commandPath,
IEnumerable<string> commandArguments)
IEnumerable<string>? commandArguments,
IDictionary<string, string>? env = null)
{
var arguments = new List<string>();

Expand Down Expand Up @@ -45,15 +44,16 @@ internal static CommandSpec CreatePackageCommandSpecUsingMuxer(
arguments.AddRange(commandArguments);
}
}
return CreateCommandSpec(host, arguments);
return CreateCommandSpec(host, arguments, env);
}

private static CommandSpec CreateCommandSpec(
string commandPath,
IEnumerable<string> commandArguments)
IEnumerable<string>? commandArguments,
IDictionary<string, string>? env = null)
{
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(commandArguments);
var escapedArgs = commandArguments is not null ? ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(commandArguments) : null;

return new CommandSpec(commandPath, escapedArgs);
return new CommandSpec(commandPath, escapedArgs, env);
}
}
4 changes: 2 additions & 2 deletions src/Cli/dotnet/CommandFactory/CommandSpec.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ namespace Microsoft.DotNet.Cli.CommandFactory;
public class CommandSpec(
string path,
string? args,
Dictionary<string, string>? environmentVariables = null)
IDictionary<string, string>? environmentVariables = null)
{
public string Path { get; } = path;

public string? Args { get; } = args;

public Dictionary<string, string> EnvironmentVariables { get; } = environmentVariables ?? [];
public IDictionary<string, string> EnvironmentVariables { get; } = environmentVariables ?? new Dictionary<string, string>();

internal void AddEnvironmentVariablesFromProject(IProject project)
{
Expand Down
Loading
Loading