Avoid repeating the first-run message if lzma archive is missing

If the LZMA archive is missing, the first-run message is printed every time.
This commit fixes that.

Split the first-run message into two pieces:

- The first-run (welcome and telemetry) message is printed only once, no matter
  whether the cache was primed or not.

- The cache-priming message is printed only if the cache is avaialble.
  Otherwise skip the cache introduction and the ccache priming operation.
This commit is contained in:
Omair Majid 2017-06-14 12:09:06 -04:00
parent 2180d92d10
commit 34b44b999b
10 changed files with 267 additions and 16 deletions

View file

@ -0,0 +1,16 @@
// 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.
namespace Microsoft.DotNet.Cli.Utils
{
public interface IReporter
{
void WriteLine(string message);
void WriteLine();
void Write(string message);
}
}

View file

@ -4,7 +4,7 @@
namespace Microsoft.DotNet.Cli.Utils
{
// Stupid-simple console manager
public class Reporter
public class Reporter : IReporter
{
private static readonly Reporter NullReporter = new Reporter(console: null);
private static object _lock = new object();

View file

@ -9,39 +9,74 @@ namespace Microsoft.DotNet.Configurer
{
public class DotnetFirstTimeUseConfigurer
{
private IReporter _reporter;
private IEnvironmentProvider _environmentProvider;
private INuGetCachePrimer _nugetCachePrimer;
private INuGetCacheSentinel _nugetCacheSentinel;
private IFirstTimeUseNoticeSentinel _firstTimeUseNoticeSentinel;
public DotnetFirstTimeUseConfigurer(
INuGetCachePrimer nugetCachePrimer,
INuGetCacheSentinel nugetCacheSentinel,
IEnvironmentProvider environmentProvider)
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
IEnvironmentProvider environmentProvider,
IReporter reporter)
{
_nugetCachePrimer = nugetCachePrimer;
_nugetCacheSentinel = nugetCacheSentinel;
_firstTimeUseNoticeSentinel = firstTimeUseNoticeSentinel;
_environmentProvider = environmentProvider;
_reporter = reporter;
}
public void Configure()
{
if(ShouldPrimeNugetCache())
if (ShouldPrintFirstTimeUseNotice())
{
PrintFirstTimeUseNotice();
}
if (ShouldPrimeNugetCache())
{
PrintNugetCachePrimeMessage();
_nugetCachePrimer.PrimeCache();
}
}
private bool ShouldPrintFirstTimeUseNotice()
{
var showFirstTimeUseNotice =
_environmentProvider.GetEnvironmentVariableAsBool("DOTNET_PRINT_TELEMETRY_MESSAGE", true);
return ShouldRunFirstRunExperience() &&
showFirstTimeUseNotice &&
!_firstTimeUseNoticeSentinel.Exists();
}
private void PrintFirstTimeUseNotice()
{
string firstTimeUseWelcomeMessage = LocalizableStrings.FirstTimeWelcomeMessage;
Reporter.Output.WriteLine();
Reporter.Output.WriteLine(firstTimeUseWelcomeMessage);
_reporter.WriteLine();
_reporter.WriteLine(firstTimeUseWelcomeMessage);
_firstTimeUseNoticeSentinel.CreateIfNotExists();
}
private bool ShouldPrimeNugetCache()
{
return ShouldRunFirstRunExperience() &&
!_nugetCachePrimer.SkipPrimingTheCache();
}
private void PrintNugetCachePrimeMessage()
{
string cachePrimeMessage = LocalizableStrings.NugetCachePrimeMessage;
_reporter.WriteLine();
_reporter.WriteLine(cachePrimeMessage);
}
private bool ShouldRunFirstRunExperience()
{
var skipFirstTimeExperience =
_environmentProvider.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false);

View file

@ -0,0 +1,49 @@
// 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 FirstTimeUseNoticeSentinel : IFirstTimeUseNoticeSentinel
{
public static readonly string SENTINEL = $"{Product.Version}.dotnetFirstUseSentinel";
private readonly IFile _file;
private string _nugetCachePath;
private string SentinelPath => Path.Combine(_nugetCachePath, SENTINEL);
public FirstTimeUseNoticeSentinel(CliFallbackFolderPathCalculator cliFallbackFolderPathCalculator) :
this(cliFallbackFolderPathCalculator.CliFallbackFolderPath, FileSystemWrapper.Default.File)
{
}
internal FirstTimeUseNoticeSentinel(string nugetCachePath, IFile file)
{
_file = file;
_nugetCachePath = nugetCachePath;
}
public bool Exists()
{
return _file.Exists(SentinelPath);
}
public void CreateIfNotExists()
{
if (!Exists())
{
_file.CreateEmptyFile(SentinelPath);
}
}
public void Dispose()
{
}
}
}

View file

@ -0,0 +1,14 @@
// 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;
namespace Microsoft.DotNet.Configurer
{
public interface IFirstTimeUseNoticeSentinel : IDisposable
{
bool Exists();
void CreateIfNotExists();
}
}

View file

@ -6,5 +6,6 @@ namespace Microsoft.DotNet.Configurer
public interface INuGetCachePrimer
{
void PrimeCache();
bool SkipPrimingTheCache();
}
}

View file

@ -126,13 +126,14 @@ Telemetry
--------------
The .NET Core tools collect usage data in order to improve your experience. The data is anonymous and does not include command-line arguments. The data is collected by Microsoft and shared with the community.
You can opt out of telemetry by setting a DOTNET_CLI_TELEMETRY_OPTOUT environment variable to 1 using your favorite shell.
You can read more about .NET Core tools telemetry @ https://aka.ms/dotnet-cli-telemetry.
Configuring...
You can read more about .NET Core tools telemetry @ https://aka.ms/dotnet-cli-telemetry.</value>
</data>
<data name="NugetCachePrimeMessage" xml:space="preserve">
<value>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.</value>
</data>
<data name="FailedToPrimeCacheError" xml:space="preserve">
<value>Failed to prime the NuGet cache. {0} failed with: {1}</value>
</data>
</root>
</root>

View file

@ -68,7 +68,7 @@ namespace Microsoft.DotNet.Configurer
_nuGetCacheSentinel.CreateIfNotExists();
}
private bool SkipPrimingTheCache()
public bool SkipPrimingTheCache()
{
return !_file.Exists(_nugetPackagesArchiver.NuGetPackagesArchive);
}

View file

@ -81,6 +81,7 @@ namespace Microsoft.DotNet.Cli
var lastArg = 0;
var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator();
using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator))
using (IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator))
{
for (; lastArg < args.Length; lastArg++)
{
@ -112,7 +113,7 @@ namespace Microsoft.DotNet.Cli
}
else
{
ConfigureDotNetForFirstTimeUse(nugetCacheSentinel, cliFallbackFolderPathCalculator);
ConfigureDotNetForFirstTimeUse(nugetCacheSentinel, firstTimeUseNoticeSentinel, cliFallbackFolderPathCalculator);
// It's the command, and we're done!
command = args[lastArg];
@ -168,6 +169,7 @@ namespace Microsoft.DotNet.Cli
private static void ConfigureDotNetForFirstTimeUse(
INuGetCacheSentinel nugetCacheSentinel,
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel,
CliFallbackFolderPathCalculator cliFallbackFolderPathCalculator)
{
using (PerfTrace.Current.CaptureTiming())
@ -184,7 +186,9 @@ namespace Microsoft.DotNet.Cli
var dotnetConfigurer = new DotnetFirstTimeUseConfigurer(
nugetCachePrimer,
nugetCacheSentinel,
environmentProvider);
firstTimeUseNoticeSentinel,
environmentProvider,
Reporter.Output);
dotnetConfigurer.Configure();
}

View file

@ -15,17 +15,102 @@ namespace Microsoft.DotNet.Configurer.UnitTests
{
private Mock<INuGetCachePrimer> _nugetCachePrimerMock;
private Mock<INuGetCacheSentinel> _nugetCacheSentinelMock;
private Mock<IFirstTimeUseNoticeSentinel> _firstTimeUseNoticeSentinelMock;
private Mock<IEnvironmentProvider> _environmentProviderMock;
private Mock<IReporter> _reporterMock;
public GivenADotnetFirstTimeUseConfigurer()
{
_nugetCachePrimerMock = new Mock<INuGetCachePrimer>();
_nugetCacheSentinelMock = new Mock<INuGetCacheSentinel>();
_firstTimeUseNoticeSentinelMock = new Mock<IFirstTimeUseNoticeSentinel>();
_environmentProviderMock = new Mock<IEnvironmentProvider>();
_reporterMock = new Mock<IReporter>();
_environmentProviderMock
.Setup(e => e.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false))
.Returns(false);
_environmentProviderMock
.Setup(e => e.GetEnvironmentVariableAsBool("DOTNET_PRINT_TELEMETRY_MESSAGE", true))
.Returns(true);
}
[Fact]
public void It_does_not_print_the_first_time_use_notice_if_the_sentinel_exists()
{
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(true);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_reporterMock.Verify(r => r.WriteLine(It.Is<string>(str => str.StartsWith("Welcome to .NET Core!"))), Times.Never);
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
}
[Fact]
public void It_does_not_print_the_first_time_use_notice_when_the_user_has_set_the_DOTNET_SKIP_FIRST_TIME_EXPERIENCE_environemnt_variable()
{
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
_environmentProviderMock
.Setup(e => e.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false))
.Returns(true);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_reporterMock.Verify(r => r.WriteLine(It.Is<string>(str => str.StartsWith("Welcome to .NET Core!"))), Times.Never);
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
}
[Fact]
public void It_does_not_print_the_first_time_use_notice_when_the_user_has_set_the_DOTNET_PRINT_TELEMETRY_MESSAGE_environemnt_variable()
{
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
_environmentProviderMock
.Setup(e => e.GetEnvironmentVariableAsBool("DOTNET_PRINT_TELEMETRY_MESSAGE", true))
.Returns(false);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_reporterMock.Verify(r => r.WriteLine(It.Is<string>(str => str.StartsWith("Welcome to .NET Core!"))), Times.Never);
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
}
[Fact]
public void It_prints_the_telemetry_if_the_sentinel_does_not_exist()
{
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_reporterMock.Verify(r => r.WriteLine(It.Is<string>(str => str.StartsWith("Welcome to .NET Core!"))));
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
}
[Fact]
@ -36,7 +121,9 @@ namespace Microsoft.DotNet.Configurer.UnitTests
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_environmentProviderMock.Object);
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -51,7 +138,26 @@ namespace Microsoft.DotNet.Configurer.UnitTests
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_environmentProviderMock.Object);
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Never);
}
[Fact]
public void It_does_not_prime_the_cache_if_cache_is_missing()
{
_nugetCachePrimerMock.Setup(n => n.SkipPrimingTheCache()).Returns(true);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -69,7 +175,9 @@ namespace Microsoft.DotNet.Configurer.UnitTests
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_environmentProviderMock.Object);
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
@ -84,11 +192,34 @@ namespace Microsoft.DotNet.Configurer.UnitTests
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_environmentProviderMock.Object);
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once);
}
[Fact]
public void It_prints_first_use_notice_and_primes_the_cache_if_the_sentinels_do_not_exist()
{
_nugetCacheSentinelMock.Setup(n => n.Exists()).Returns(false);
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
_nugetCachePrimerMock.Object,
_nugetCacheSentinelMock.Object,
_firstTimeUseNoticeSentinelMock.Object,
_environmentProviderMock.Object,
_reporterMock.Object);
dotnetFirstTimeUseConfigurer.Configure();
_reporterMock.Verify(r => r.WriteLine(It.Is<string>(str => str.StartsWith("Welcome to .NET Core!"))));
_reporterMock.Verify(r => r.WriteLine(It.Is<string>(str => str.StartsWith("Configuring"))));
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once);
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
}
}
}