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:
parent
2180d92d10
commit
34b44b999b
10 changed files with 267 additions and 16 deletions
16
src/Microsoft.DotNet.Cli.Utils/IReporter.cs
Normal file
16
src/Microsoft.DotNet.Cli.Utils/IReporter.cs
Normal 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);
|
||||
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -6,5 +6,6 @@ namespace Microsoft.DotNet.Configurer
|
|||
public interface INuGetCachePrimer
|
||||
{
|
||||
void PrimeCache();
|
||||
bool SkipPrimingTheCache();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Microsoft.DotNet.Configurer
|
|||
_nuGetCacheSentinel.CreateIfNotExists();
|
||||
}
|
||||
|
||||
private bool SkipPrimingTheCache()
|
||||
public bool SkipPrimingTheCache()
|
||||
{
|
||||
return !_file.Exists(_nugetPackagesArchiver.NuGetPackagesArchive);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue