Merge pull request #7266 from livarcocc/show_first_run_message

Show first run notice even when CLI is installed by native installers.
This commit is contained in:
Livar 2017-07-26 19:29:05 -07:00 committed by GitHub
commit e5786487ec
9 changed files with 206 additions and 19 deletions

View file

@ -45,12 +45,14 @@ namespace Microsoft.DotNet.Configurer
{ {
PrintUnauthorizedAccessMessage(); PrintUnauthorizedAccessMessage();
} }
else
{
PrintNugetCachePrimeMessage(); PrintNugetCachePrimeMessage();
_nugetCachePrimer.PrimeCache(); _nugetCachePrimer.PrimeCache();
} }
} }
}
private bool ShouldPrintFirstTimeUseNotice() private bool ShouldPrintFirstTimeUseNotice()
{ {
@ -81,6 +83,8 @@ namespace Microsoft.DotNet.Configurer
private bool ShouldPrimeNugetCache() private bool ShouldPrimeNugetCache()
{ {
return ShouldRunFirstRunExperience() && return ShouldRunFirstRunExperience() &&
!_nugetCacheSentinel.Exists() &&
!_nugetCacheSentinel.InProgressSentinelAlreadyExists() &&
!_nugetCachePrimer.SkipPrimingTheCache(); !_nugetCachePrimer.SkipPrimingTheCache();
} }
@ -96,9 +100,7 @@ namespace Microsoft.DotNet.Configurer
var skipFirstTimeExperience = var skipFirstTimeExperience =
_environmentProvider.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false); _environmentProvider.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false);
return !skipFirstTimeExperience && return !skipFirstTimeExperience;
!_nugetCacheSentinel.Exists() &&
!_nugetCacheSentinel.InProgressSentinelAlreadyExists();
} }
} }
} }

View file

@ -0,0 +1,26 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.IO;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Configuration;
namespace Microsoft.DotNet.Configurer
{
public class NoOpFirstTimeUseNoticeSentinel : IFirstTimeUseNoticeSentinel
{
public bool Exists()
{
return true;
}
public void CreateIfNotExists()
{
}
public void Dispose()
{
}
}
}

View file

@ -45,7 +45,7 @@ namespace Microsoft.DotNet.Configurer
public bool InProgressSentinelAlreadyExists() public bool InProgressSentinelAlreadyExists()
{ {
return CouldNotGetAHandleToTheInProgressSentinel(); return CouldNotGetAHandleToTheInProgressSentinel() && !UnauthorizedAccess;
} }
public bool Exists() public bool Exists()

View file

@ -81,8 +81,10 @@ namespace Microsoft.DotNet.Cli
var lastArg = 0; var lastArg = 0;
var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator(); var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator();
using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator)) using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator))
using (IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator)) using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator))
{ {
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel;
for (; lastArg < args.Length; lastArg++) for (; lastArg < args.Length; lastArg++)
{ {
if (IsArg(args[lastArg], "d", "diagnostics")) if (IsArg(args[lastArg], "d", "diagnostics"))
@ -113,10 +115,19 @@ namespace Microsoft.DotNet.Cli
} }
else else
{ {
ConfigureDotNetForFirstTimeUse(nugetCacheSentinel, firstTimeUseNoticeSentinel, cliFallbackFolderPathCalculator);
// It's the command, and we're done! // It's the command, and we're done!
command = args[lastArg]; command = args[lastArg];
if (IsDotnetBeingInvokedFromNativeInstaller(command))
{
firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
}
ConfigureDotNetForFirstTimeUse(
nugetCacheSentinel,
firstTimeUseNoticeSentinel,
cliFallbackFolderPathCalculator);
break; break;
} }
} }
@ -164,7 +175,11 @@ namespace Microsoft.DotNet.Cli
} }
return exitCode; return exitCode;
}
private static bool IsDotnetBeingInvokedFromNativeInstaller(string command)
{
return command == "internal-reportinstallsuccess";
} }
private static void ConfigureDotNetForFirstTimeUse( private static void ConfigureDotNetForFirstTimeUse(

View file

@ -37,7 +37,7 @@ namespace Microsoft.DotNet.Cli
public Telemetry(IFirstTimeUseNoticeSentinel sentinel) : this(sentinel, null) { } public Telemetry(IFirstTimeUseNoticeSentinel sentinel) : this(sentinel, null) { }
public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId) public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId, bool blockThreadInitialization = false)
{ {
Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel); Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel);
@ -49,9 +49,16 @@ namespace Microsoft.DotNet.Cli
// Store the session ID in a static field so that it can be reused // Store the session ID in a static field so that it can be reused
CurrentSessionId = sessionId ?? Guid.NewGuid().ToString(); CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();
if (blockThreadInitialization)
{
InitializeTelemetry();
}
else
{
//initialize in task to offload to parallel thread //initialize in task to offload to parallel thread
_trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry()); _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry());
} }
}
private bool PermissionExists(IFirstTimeUseNoticeSentinel sentinel) private bool PermissionExists(IFirstTimeUseNoticeSentinel sentinel)
{ {
@ -126,9 +133,9 @@ namespace Microsoft.DotNet.Cli
_client.TrackEvent(eventName, eventProperties, eventMeasurements); _client.TrackEvent(eventName, eventProperties, eventMeasurements);
_client.Flush(); _client.Flush();
} }
catch (Exception) catch (Exception e)
{ {
Debug.Fail("Exception during TrackEventTask"); Debug.Fail(e.ToString());
} }
} }

View file

@ -43,7 +43,7 @@ namespace Microsoft.DotNet.Cli
{ {
var sessionId = var sessionId =
Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName); Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName);
telemetry = new Telemetry(new FirstTimeUseNoticeSentinel(new CliFallbackFolderPathCalculator()), sessionId); telemetry = new Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId, blockThreadInitialization: true);
} }
public bool Enabled => telemetry.Enabled; public bool Enabled => telemetry.Enabled;

View file

@ -233,5 +233,73 @@ namespace Microsoft.DotNet.Configurer.UnitTests
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once); _nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once);
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never); _reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
} }
[Fact]
public void It_prints_the_first_time_use_notice_if_the_cache_sentinel_exists_but_the_first_notice_sentinel_does_not()
{
_nugetCacheSentinelMock.Setup(n => n.Exists()).Returns(true);
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath);
dotnetFirstTimeUseConfigurer.Configure();
_reporterMock.Verify(r =>
r.WriteLine(It.Is<string>(str => str == LocalizableStrings.FirstTimeWelcomeMessage)));
_reporterMock.Verify(
r => r.WriteLine(It.Is<string>(str => str == LocalizableStrings.NugetCachePrimeMessage)),
Times.Never);
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
}
[Fact]
public void It_prints_the_unauthorized_notice_if_the_cache_sentinel_reports_Unauthorized()
{
_nugetCacheSentinelMock.Setup(n => n.UnauthorizedAccess).Returns(true);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath);
dotnetFirstTimeUseConfigurer.Configure();
_reporterMock.Verify(r =>
r.WriteLine(It.Is<string>(str => str == LocalizableStrings.FirstTimeWelcomeMessage)));
_reporterMock.Verify(r =>
r.WriteLine(It.Is<string>(str =>
str == string.Format(LocalizableStrings.UnauthorizedAccessMessage, CliFallbackFolderPath))));
_reporterMock.Verify(
r => r.WriteLine(It.Is<string>(str => str == LocalizableStrings.NugetCachePrimeMessage)),
Times.Never);
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
}
[Fact]
public void It_does_not_prime_the_cache_if_the_cache_sentinel_reports_Unauthorized()
{
_nugetCacheSentinelMock.Setup(n => n.UnauthorizedAccess).Returns(true);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object,
CliFallbackFolderPath);
dotnetFirstTimeUseConfigurer.Configure();
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Never);
}
} }
} }

View file

@ -70,6 +70,18 @@ namespace Microsoft.DotNet.Configurer.UnitTests
nugetCacheSentinel.InProgressSentinelAlreadyExists().Should().BeTrue(); nugetCacheSentinel.InProgressSentinelAlreadyExists().Should().BeTrue();
} }
[Fact]
public void It_returns_false_to_the_in_progress_sentinel_already_exists_when_it_fails_to_get_a_handle_to_it_but_it_failed_because_it_was_unauthorized()
{
var fileMock = new FileMock();
var directoryMock = new DirectoryMock();
fileMock.InProgressSentinel = null;
var nugetCacheSentinel =
new NuGetCacheSentinel(NUGET_CACHE_PATH, fileMock, directoryMock);
nugetCacheSentinel.InProgressSentinelAlreadyExists().Should().BeFalse();
}
[Fact] [Fact]
public void It_returns_false_to_the_in_progress_sentinel_already_exists_when_it_succeeds_in_getting_a_handle_to_it() public void It_returns_false_to_the_in_progress_sentinel_already_exists_when_it_succeeds_in_getting_a_handle_to_it()
{ {

View file

@ -20,15 +20,17 @@ namespace Microsoft.DotNet.Tests
private static CommandResult _firstDotnetNonVerbUseCommandResult; private static CommandResult _firstDotnetNonVerbUseCommandResult;
private static CommandResult _firstDotnetVerbUseCommandResult; private static CommandResult _firstDotnetVerbUseCommandResult;
private static DirectoryInfo _nugetFallbackFolder; private static DirectoryInfo _nugetFallbackFolder;
private static DirectoryInfo _dotDotnetFolder;
private static string _testDirectory;
static GivenThatTheUserIsRunningDotNetForTheFirstTime() static GivenThatTheUserIsRunningDotNetForTheFirstTime()
{ {
var testDirectory = TestAssets.CreateTestDirectory("Dotnet_first_time_experience_tests"); _testDirectory = TestAssets.CreateTestDirectory("Dotnet_first_time_experience_tests").FullName;
var testNuGetHome = Path.Combine(testDirectory.FullName, "nuget_home"); var testNuGetHome = Path.Combine(_testDirectory, "nuget_home");
var cliTestFallbackFolder = Path.Combine(testNuGetHome, ".dotnet", "NuGetFallbackFolder"); var cliTestFallbackFolder = Path.Combine(testNuGetHome, ".dotnet", "NuGetFallbackFolder");
var command = new DotnetCommand() var command = new DotnetCommand()
.WithWorkingDirectory(testDirectory); .WithWorkingDirectory(_testDirectory);
command.Environment["HOME"] = testNuGetHome; command.Environment["HOME"] = testNuGetHome;
command.Environment["USERPROFILE"] = testNuGetHome; command.Environment["USERPROFILE"] = testNuGetHome;
command.Environment["APPDATA"] = testNuGetHome; command.Environment["APPDATA"] = testNuGetHome;
@ -40,6 +42,7 @@ namespace Microsoft.DotNet.Tests
_firstDotnetVerbUseCommandResult = command.ExecuteWithCapturedOutput("new --debug:ephemeral-hive"); _firstDotnetVerbUseCommandResult = command.ExecuteWithCapturedOutput("new --debug:ephemeral-hive");
_nugetFallbackFolder = new DirectoryInfo(cliTestFallbackFolder); _nugetFallbackFolder = new DirectoryInfo(cliTestFallbackFolder);
_dotDotnetFolder = new DirectoryInfo(Path.Combine(testNuGetHome, ".dotnet"));
} }
[Fact] [Fact]
@ -77,6 +80,60 @@ namespace Microsoft.DotNet.Tests
.HaveFile($"{GetDotnetVersion()}.dotnetSentinel"); .HaveFile($"{GetDotnetVersion()}.dotnetSentinel");
} }
[Fact]
public void ItCreatesAFirstUseSentinelFileUnderTheDotDotNetFolder()
{
_dotDotnetFolder
.Should()
.HaveFile($"{GetDotnetVersion()}.dotnetFirstUseSentinel");
}
[Fact]
public void ItDoesNotCreateAFirstUseSentinelFileUnderTheDotDotNetFolderWhenInternalReportInstallSuccessIsInvoked()
{
var emptyHome = Path.Combine(_testDirectory, "empty_home");
var command = new DotnetCommand()
.WithWorkingDirectory(_testDirectory);
command.Environment["HOME"] = emptyHome;
command.Environment["USERPROFILE"] = emptyHome;
command.Environment["APPDATA"] = emptyHome;
command.Environment["DOTNET_CLI_TEST_FALLBACKFOLDER"] = _nugetFallbackFolder.FullName;
command.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "";
// Disable to prevent the creation of the .dotnet folder by optimizationdata.
command.Environment["DOTNET_DISABLE_MULTICOREJIT"] = "true";
command.Environment["SkipInvalidConfigurations"] = "true";
command.ExecuteWithCapturedOutput("internal-reportinstallsuccess test").Should().Pass();
var emptyHomeFolder = new DirectoryInfo(Path.Combine(emptyHome, ".dotnet"));
emptyHomeFolder.Should().NotExist();
}
[Fact]
public void ItShowsTheTelemetryNoticeWhenInvokingACommandAfterInternalReportInstallSuccessHasBeenInvoked()
{
var newHome = Path.Combine(_testDirectory, "new_home");
var newHomeFolder = new DirectoryInfo(Path.Combine(newHome, ".dotnet"));
var command = new DotnetCommand()
.WithWorkingDirectory(_testDirectory);
command.Environment["HOME"] = newHome;
command.Environment["USERPROFILE"] = newHome;
command.Environment["APPDATA"] = newHome;
command.Environment["DOTNET_CLI_TEST_FALLBACKFOLDER"] = _nugetFallbackFolder.FullName;
command.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "";
command.Environment["SkipInvalidConfigurations"] = "true";
command.ExecuteWithCapturedOutput("internal-reportinstallsuccess test").Should().Pass();
var result = command.ExecuteWithCapturedOutput("new --debug:ephemeral-hive");
result.StdOut
.Should()
.ContainVisuallySameFragment(Configurer.LocalizableStrings.FirstTimeWelcomeMessage);
}
[Fact] [Fact]
public void ItRestoresTheNuGetPackagesToTheNuGetCacheFolder() public void ItRestoresTheNuGetPackagesToTheNuGetCacheFolder()
{ {