diff --git a/src/dotnet/Program.cs b/src/dotnet/Program.cs index 7aeb94c89..6a2f8b751 100644 --- a/src/dotnet/Program.cs +++ b/src/dotnet/Program.cs @@ -55,11 +55,9 @@ namespace Microsoft.DotNet.Cli try { - ConfigureDotNetForFirstTimeUse(); - using (PerfTrace.Current.CaptureTiming()) { - return ProcessArgs(args, new Telemetry()); + return ProcessArgs(args); } } catch (GracefulException e) @@ -78,7 +76,7 @@ namespace Microsoft.DotNet.Cli } } - internal static int ProcessArgs(string[] args, ITelemetry telemetryClient) + internal static int ProcessArgs(string[] args, ITelemetry telemetryClient = null) { // CommandLineApplication is a bit restrictive, so we parse things ourselves here. Individual apps should use CLA. @@ -86,6 +84,7 @@ namespace Microsoft.DotNet.Cli var success = true; var command = string.Empty; var lastArg = 0; + for (; lastArg < args.Length; lastArg++) { if (IsArg(args[lastArg], "v", "verbose")) @@ -114,6 +113,8 @@ namespace Microsoft.DotNet.Cli } else { + ConfigureDotNetForFirstTimeUse(); + // It's the command, and we're done! command = args[lastArg]; break; @@ -125,6 +126,9 @@ namespace Microsoft.DotNet.Cli return 1; } + if (telemetryClient == null) + telemetryClient = new Telemetry(); + var appArgs = (lastArg + 1) >= args.Length ? Enumerable.Empty() : args.Skip(lastArg + 1).ToArray(); if (verbose.HasValue) @@ -178,6 +182,9 @@ namespace Microsoft.DotNet.Cli environmentProvider); dotnetConfigurer.Configure(); + + if (!File.Exists(Telemetry.TelemetrySentinel)) + File.Create(Telemetry.TelemetrySentinel); } } } diff --git a/src/dotnet/Telemetry.cs b/src/dotnet/Telemetry.cs index 838139b50..9b8e5acd4 100644 --- a/src/dotnet/Telemetry.cs +++ b/src/dotnet/Telemetry.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Threading.Tasks; using Microsoft.ApplicationInsights; using Microsoft.DotNet.Cli.Utils; @@ -19,6 +20,8 @@ namespace Microsoft.DotNet.Cli private Dictionary _commonMeasurements = null; private Task _trackEventTask = null; + internal static readonly string TelemetrySentinel = Path.Combine(ApplicationEnvironment.ApplicationBasePath, $"{Product.Version}.dotnetTelemetry"); + private const string InstrumentationKey = "74cc1c9e-3e6e-4d05-b3fc-dde9101d0254"; private const string TelemetryOptout = "DOTNET_CLI_TELEMETRY_OPTOUT"; private const string TelemetryProfileEnvironmentVariable = "DOTNET_CLI_TELEMETRY_PROFILE"; @@ -32,7 +35,7 @@ namespace Microsoft.DotNet.Cli public Telemetry() { - Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout); + Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(); if (!Enabled) { @@ -42,6 +45,11 @@ namespace Microsoft.DotNet.Cli //initialize in task to offload to parallel thread _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry()); } + + private bool PermissionExists() + { + return File.Exists(TelemetrySentinel); + } public void TrackEvent(string eventName, IDictionary properties, IDictionary measurements) { diff --git a/test/dotnet.Tests/GivenThatTheUserIsRunningDoNetForTheFirstTime.cs b/test/dotnet.Tests/GivenThatTheUserIsRunningDoNetForTheFirstTime.cs index 5df9c2c8b..5756411ce 100644 --- a/test/dotnet.Tests/GivenThatTheUserIsRunningDoNetForTheFirstTime.cs +++ b/test/dotnet.Tests/GivenThatTheUserIsRunningDoNetForTheFirstTime.cs @@ -15,7 +15,8 @@ namespace Microsoft.DotNet.Tests { public class GivenThatTheUserIsRunningDotNetForTheFirstTime : TestBase { - private static CommandResult _firstDotnetUseCommandResult; + private static CommandResult _firstDotnetInfoUseCommandResult; + private static CommandResult _firstDotnetNewUseCommandResult; private static DirectoryInfo _nugetCacheFolder; static GivenThatTheUserIsRunningDotNetForTheFirstTime() @@ -28,7 +29,8 @@ namespace Microsoft.DotNet.Tests command.Environment["NUGET_PACKAGES"] = testNugetCache; command.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = ""; - _firstDotnetUseCommandResult = command.ExecuteWithCapturedOutput("new"); + _firstDotnetInfoUseCommandResult = command.ExecuteWithCapturedOutput("--info"); + _firstDotnetNewUseCommandResult = command.ExecuteWithCapturedOutput("new"); _nugetCacheFolder = new DirectoryInfo(testNugetCache); } @@ -36,7 +38,15 @@ namespace Microsoft.DotNet.Tests [Fact] public void Using_dotnet_for_the_first_time_succeeds() { - _firstDotnetUseCommandResult.Should().Pass(); + _firstDotnetNewUseCommandResult.Should().Pass(); + } + + [Fact] + public void Using_dotnet_for_the_first_time_with_non_verbs_doesnt_print_eula() + { + const string firstTimeUseWelcomeMessage = @".NET Command Line Tools"; + + _firstDotnetInfoUseCommandResult.StdOut.Should().StartWith(firstTimeUseWelcomeMessage); } [Fact] @@ -54,12 +64,12 @@ Configuring... ------------------- A command is running to initially populate your local package cache, to improve restore speed and enable offline access. This command will take up to a minute to complete and will only happen once."; - _firstDotnetUseCommandResult.StdOut.Should().StartWith(firstTimeUseWelcomeMessage); + _firstDotnetNewUseCommandResult.StdOut.Should().StartWith(firstTimeUseWelcomeMessage); } [Fact] public void It_restores_the_nuget_packages_to_the_nuget_cache_folder() - { + { _nugetCacheFolder.Should().HaveFile($"{GetDotnetVersion()}.dotnetSentinel"); }