From d40d128fed9775e0c7e09965ebd0a0d0348d68cf Mon Sep 17 00:00:00 2001 From: Justin Emgarten Date: Mon, 24 Jul 2017 18:41:27 -0700 Subject: [PATCH 01/20] Updating NuGet to 4.3.0-rtm-4324 --- build/Microsoft.DotNet.Cli.DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Microsoft.DotNet.Cli.DependencyVersions.props b/build/Microsoft.DotNet.Cli.DependencyVersions.props index b98daae27..1bcda2135 100644 --- a/build/Microsoft.DotNet.Cli.DependencyVersions.props +++ b/build/Microsoft.DotNet.Cli.DependencyVersions.props @@ -6,7 +6,7 @@ 2.3.2-beta1-61921-05 4.2.0-rc-170630-0 1.1.0-alpha-20170721-6 - 4.3.0-rtm-4315 + 4.3.0-rtm-4324 1.0.0-alpha-20170516-2-509 15.0.0 1.0.0-beta2-20170629-269 From 20f0dac6b212895475a38b9b0be246d5edaf415c Mon Sep 17 00:00:00 2001 From: Livar Cunha Date: Tue, 25 Jul 2017 22:30:36 -0700 Subject: [PATCH 02/20] Making a change that will cause the first run notice to always show up in the first run of the CLI, even when it is installed by native installers. --- .../DotnetFirstTimeUseConfigurer.cs | 14 ++-- .../NoOpFirstTimeUseNoticeSentinel.cs | 26 +++++++ .../NuGetCacheSentinel.cs | 2 +- src/dotnet/Program.cs | 21 +++++- .../GivenADotnetFirstTimeUseConfigurer.cs | 68 +++++++++++++++++++ .../GivenANuGetCacheSentinel.cs | 12 ++++ ...atTheUserIsRunningDotNetForTheFirstTime.cs | 61 ++++++++++++++++- 7 files changed, 191 insertions(+), 13 deletions(-) create mode 100644 src/Microsoft.DotNet.Configurer/NoOpFirstTimeUseNoticeSentinel.cs diff --git a/src/Microsoft.DotNet.Configurer/DotnetFirstTimeUseConfigurer.cs b/src/Microsoft.DotNet.Configurer/DotnetFirstTimeUseConfigurer.cs index e13637c47..9a89bcb6f 100644 --- a/src/Microsoft.DotNet.Configurer/DotnetFirstTimeUseConfigurer.cs +++ b/src/Microsoft.DotNet.Configurer/DotnetFirstTimeUseConfigurer.cs @@ -45,10 +45,12 @@ namespace Microsoft.DotNet.Configurer { PrintUnauthorizedAccessMessage(); } + else + { + PrintNugetCachePrimeMessage(); - PrintNugetCachePrimeMessage(); - - _nugetCachePrimer.PrimeCache(); + _nugetCachePrimer.PrimeCache(); + } } } @@ -81,6 +83,8 @@ namespace Microsoft.DotNet.Configurer private bool ShouldPrimeNugetCache() { return ShouldRunFirstRunExperience() && + !_nugetCacheSentinel.Exists() && + !_nugetCacheSentinel.InProgressSentinelAlreadyExists() && !_nugetCachePrimer.SkipPrimingTheCache(); } @@ -96,9 +100,7 @@ namespace Microsoft.DotNet.Configurer var skipFirstTimeExperience = _environmentProvider.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false); - return !skipFirstTimeExperience && - !_nugetCacheSentinel.Exists() && - !_nugetCacheSentinel.InProgressSentinelAlreadyExists(); + return !skipFirstTimeExperience; } } } diff --git a/src/Microsoft.DotNet.Configurer/NoOpFirstTimeUseNoticeSentinel.cs b/src/Microsoft.DotNet.Configurer/NoOpFirstTimeUseNoticeSentinel.cs new file mode 100644 index 000000000..a801d4ee6 --- /dev/null +++ b/src/Microsoft.DotNet.Configurer/NoOpFirstTimeUseNoticeSentinel.cs @@ -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() + { + } + } +} diff --git a/src/Microsoft.DotNet.Configurer/NuGetCacheSentinel.cs b/src/Microsoft.DotNet.Configurer/NuGetCacheSentinel.cs index def087615..bfc7a29d9 100644 --- a/src/Microsoft.DotNet.Configurer/NuGetCacheSentinel.cs +++ b/src/Microsoft.DotNet.Configurer/NuGetCacheSentinel.cs @@ -45,7 +45,7 @@ namespace Microsoft.DotNet.Configurer public bool InProgressSentinelAlreadyExists() { - return CouldNotGetAHandleToTheInProgressSentinel(); + return CouldNotGetAHandleToTheInProgressSentinel() && !UnauthorizedAccess; } public bool Exists() diff --git a/src/dotnet/Program.cs b/src/dotnet/Program.cs index 4611ec7b7..5f44524d4 100644 --- a/src/dotnet/Program.cs +++ b/src/dotnet/Program.cs @@ -81,8 +81,10 @@ namespace Microsoft.DotNet.Cli var lastArg = 0; var cliFallbackFolderPathCalculator = new 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++) { if (IsArg(args[lastArg], "d", "diagnostics")) @@ -113,10 +115,19 @@ namespace Microsoft.DotNet.Cli } else { - ConfigureDotNetForFirstTimeUse(nugetCacheSentinel, firstTimeUseNoticeSentinel, cliFallbackFolderPathCalculator); - // It's the command, and we're done! command = args[lastArg]; + + if (IsDotnetBeingInvokedFromNativeInstaller(command)) + { + firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel(); + } + + ConfigureDotNetForFirstTimeUse( + nugetCacheSentinel, + firstTimeUseNoticeSentinel, + cliFallbackFolderPathCalculator); + break; } } @@ -164,7 +175,11 @@ namespace Microsoft.DotNet.Cli } return exitCode; + } + private static bool IsDotnetBeingInvokedFromNativeInstaller(string command) + { + return command == "internal-reportinstallsuccess"; } private static void ConfigureDotNetForFirstTimeUse( diff --git a/test/Microsoft.DotNet.Configurer.UnitTests/GivenADotnetFirstTimeUseConfigurer.cs b/test/Microsoft.DotNet.Configurer.UnitTests/GivenADotnetFirstTimeUseConfigurer.cs index 24ed0a67e..a9a0e904b 100644 --- a/test/Microsoft.DotNet.Configurer.UnitTests/GivenADotnetFirstTimeUseConfigurer.cs +++ b/test/Microsoft.DotNet.Configurer.UnitTests/GivenADotnetFirstTimeUseConfigurer.cs @@ -233,5 +233,73 @@ namespace Microsoft.DotNet.Configurer.UnitTests _nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once); _reporterMock.Verify(r => r.Write(It.IsAny()), 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(str => str == LocalizableStrings.FirstTimeWelcomeMessage))); + _reporterMock.Verify( + r => r.WriteLine(It.Is(str => str == LocalizableStrings.NugetCachePrimeMessage)), + Times.Never); + _reporterMock.Verify(r => r.Write(It.IsAny()), 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(str => str == LocalizableStrings.FirstTimeWelcomeMessage))); + _reporterMock.Verify(r => + r.WriteLine(It.Is(str => + str == string.Format(LocalizableStrings.UnauthorizedAccessMessage, CliFallbackFolderPath)))); + _reporterMock.Verify( + r => r.WriteLine(It.Is(str => str == LocalizableStrings.NugetCachePrimeMessage)), + Times.Never); + _reporterMock.Verify(r => r.Write(It.IsAny()), 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); + } } } diff --git a/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCacheSentinel.cs b/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCacheSentinel.cs index 97397d79d..23ae45c4a 100644 --- a/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCacheSentinel.cs +++ b/test/Microsoft.DotNet.Configurer.UnitTests/GivenANuGetCacheSentinel.cs @@ -70,6 +70,18 @@ namespace Microsoft.DotNet.Configurer.UnitTests 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] public void It_returns_false_to_the_in_progress_sentinel_already_exists_when_it_succeeds_in_getting_a_handle_to_it() { diff --git a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs index 026053261..6fd6e0dfe 100644 --- a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs +++ b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs @@ -20,15 +20,17 @@ namespace Microsoft.DotNet.Tests private static CommandResult _firstDotnetNonVerbUseCommandResult; private static CommandResult _firstDotnetVerbUseCommandResult; private static DirectoryInfo _nugetFallbackFolder; + private static DirectoryInfo _dotDotnetFolder; + private static string _testDirectory; static GivenThatTheUserIsRunningDotNetForTheFirstTime() { - var testDirectory = TestAssets.CreateTestDirectory("Dotnet_first_time_experience_tests"); - var testNuGetHome = Path.Combine(testDirectory.FullName, "nuget_home"); + _testDirectory = TestAssets.CreateTestDirectory("Dotnet_first_time_experience_tests").FullName; + var testNuGetHome = Path.Combine(_testDirectory, "nuget_home"); var cliTestFallbackFolder = Path.Combine(testNuGetHome, ".dotnet", "NuGetFallbackFolder"); var command = new DotnetCommand() - .WithWorkingDirectory(testDirectory); + .WithWorkingDirectory(_testDirectory); command.Environment["HOME"] = testNuGetHome; command.Environment["USERPROFILE"] = testNuGetHome; command.Environment["APPDATA"] = testNuGetHome; @@ -40,6 +42,7 @@ namespace Microsoft.DotNet.Tests _firstDotnetVerbUseCommandResult = command.ExecuteWithCapturedOutput("new --debug:ephemeral-hive"); _nugetFallbackFolder = new DirectoryInfo(cliTestFallbackFolder); + _dotDotnetFolder = new DirectoryInfo(Path.Combine(testNuGetHome, ".dotnet")); } [Fact] @@ -77,6 +80,58 @@ namespace Microsoft.DotNet.Tests .HaveFile($"{GetDotnetVersion()}.dotnetSentinel"); } + [Fact] + public void ItCreatesAFirstUseSentinelFileUnderTheDotDotNetFolder() + { + _dotDotnetFolder + .Should() + .HaveFile($"{GetDotnetVersion()}.dotnetFirstUseSentinel"); + } + + [Fact] + public void ItDoesNotCreateAFirstUseSentinelFileUnderTheDotDotNetFolderWhenInternalReportInstallSuccessIsInvoked() + { + 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(); + + newHomeFolder.Should().NotHaveFile($"{GetDotnetVersion()}.dotnetFirstUseSentinel"); + } + + [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] public void ItRestoresTheNuGetPackagesToTheNuGetCacheFolder() { From d4a9de3778c894c69a71b9e15179bb0fb0ba0736 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbyn=C4=9Bk=20Sailer?= Date: Wed, 26 Jul 2017 09:41:49 +0200 Subject: [PATCH 03/20] LOC CHECKIN | cli-release/2.0.0 | 20170726 --- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.cs.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.de.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.es.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.fr.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.it.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ja.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ko.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pl.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pt-BR.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ru.xlf | 4 ++-- src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.tr.xlf | 4 ++-- .../xlf/LocalizableStrings.zh-Hans.xlf | 4 ++-- .../xlf/LocalizableStrings.zh-Hant.xlf | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.cs.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.cs.xlf index c73270522..50f4e8538 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.cs.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.cs.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Rozbalení Decompressing - Decompressing + Dekomprese diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.de.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.de.xlf index 96d5d68ca..137ac4d1e 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.de.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.de.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Aufklappen Decompressing - Decompressing + Dekomprimieren diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.es.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.es.xlf index 926da184e..0fd56bad0 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.es.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.es.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Expandiendo Decompressing - Decompressing + Descomprimiendo diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.fr.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.fr.xlf index 5524d9821..c39aba992 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.fr.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.fr.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Développement Decompressing - Decompressing + Décompression diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.it.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.it.xlf index 6c1c15533..be414bd1a 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.it.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.it.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Espansione Decompressing - Decompressing + Decompressione diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ja.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ja.xlf index e00b949fb..f70a95565 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ja.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ja.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + 展開中 Decompressing - Decompressing + 圧縮解除中 diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ko.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ko.xlf index fb515905d..bf7de8172 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ko.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ko.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + 확장하는 중 Decompressing - Decompressing + 압축을 푸는 중 diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pl.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pl.xlf index bfe8b9a35..9772365f1 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pl.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pl.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Rozwijanie Decompressing - Decompressing + Dekompresja diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pt-BR.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pt-BR.xlf index 9e6cb642c..1b3fad0d8 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.pt-BR.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Expandindo Decompressing - Decompressing + Descompactando diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ru.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ru.xlf index c09a74f61..2a8c6091d 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ru.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.ru.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Идет расширение Decompressing - Decompressing + Идет извлечение diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.tr.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.tr.xlf index 8be3dbe6a..f1fde5d99 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.tr.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.tr.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + Genişletme Decompressing - Decompressing + Daraltma diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hans.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hans.xlf index aab479ac3..65ab99d0b 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hans.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + 正在扩展 Decompressing - Decompressing + 正在解压缩 diff --git a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hant.xlf b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hant.xlf index a333565e1..b5c24cae7 100644 --- a/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Microsoft.DotNet.Archive/xlf/LocalizableStrings.zh-Hant.xlf @@ -4,12 +4,12 @@ Expanding - Expanding + 正在展開 Decompressing - Decompressing + 正在解壓縮 From e4a52e57b9b93cbae18da9074de23e377e843e57 Mon Sep 17 00:00:00 2001 From: Livar Date: Wed, 26 Jul 2017 09:21:15 -0700 Subject: [PATCH 04/20] Update SDK to 2.0.0-preview3-20170726-3 --- build/DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index 3c2f93ce8..a76e5ad6b 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -12,7 +12,7 @@ - 2.0.0-preview3-20170721-7 + 2.0.0-preview3-20170726-3 $(CLI_NETSDK_Version) 4.3.0-rtm-4324 From 015af46c997cad09dff609b9a87e44b09e974f8a Mon Sep 17 00:00:00 2001 From: Livar Cunha Date: Wed, 26 Jul 2017 10:29:10 -0700 Subject: [PATCH 05/20] Fixing a test that fails due to a race condition, because the .dotnet folder might not have been created yet. --- ...venThatTheUserIsRunningDotNetForTheFirstTime.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs index 6fd6e0dfe..f323a7d8d 100644 --- a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs +++ b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs @@ -91,21 +91,23 @@ namespace Microsoft.DotNet.Tests [Fact] public void ItDoesNotCreateAFirstUseSentinelFileUnderTheDotDotNetFolderWhenInternalReportInstallSuccessIsInvoked() { - var newHome = Path.Combine(_testDirectory, "new_home"); - var newHomeFolder = new DirectoryInfo(Path.Combine(newHome, ".dotnet")); + var emptyHome = Path.Combine(_testDirectory, "empty_home"); var command = new DotnetCommand() .WithWorkingDirectory(_testDirectory); - command.Environment["HOME"] = newHome; - command.Environment["USERPROFILE"] = newHome; - command.Environment["APPDATA"] = newHome; + 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(); - newHomeFolder.Should().NotHaveFile($"{GetDotnetVersion()}.dotnetFirstUseSentinel"); + var emptyHomeFolder = new DirectoryInfo(Path.Combine(emptyHome, ".dotnet")); + emptyHomeFolder.Should().NotExist(); } [Fact] From 6541a16a384b76f31a33bdc0b681172f2e7395e3 Mon Sep 17 00:00:00 2001 From: Livar Cunha Date: Wed, 26 Jul 2017 15:33:48 -0700 Subject: [PATCH 06/20] Changing the FirstNoticeSentinel used by the internal-reportinstallsuccess command. --- .../InternalReportinstallsuccessCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs index f3ac8702f..14f940c07 100644 --- a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs +++ b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs @@ -43,7 +43,7 @@ namespace Microsoft.DotNet.Cli { var sessionId = Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName); - telemetry = new Telemetry(new FirstTimeUseNoticeSentinel(new CliFallbackFolderPathCalculator()), sessionId); + telemetry = new Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId); } public bool Enabled => telemetry.Enabled; From c8590354a7c2d1c7ad2f1d3312edc141ef26c275 Mon Sep 17 00:00:00 2001 From: William Li Date: Wed, 26 Jul 2017 16:03:59 -0700 Subject: [PATCH 07/20] Add block thread constructor --- src/dotnet/Telemetry.cs | 26 +++++++++++++++++-- .../InternalReportinstallsuccessCommand.cs | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/dotnet/Telemetry.cs b/src/dotnet/Telemetry.cs index 457e6f7af..cf5437a19 100644 --- a/src/dotnet/Telemetry.cs +++ b/src/dotnet/Telemetry.cs @@ -53,6 +53,28 @@ namespace Microsoft.DotNet.Cli _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry()); } + public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId, bool blockThreadInitialization) + { + Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel); + + if (!Enabled) + { + return; + } + + // Store the session ID in a static field so that it can be reused + CurrentSessionId = sessionId ?? Guid.NewGuid().ToString(); + + if (blockThreadInitialization) + { + InitializeTelemetry(); + } + else + { + _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry()); + } + } + private bool PermissionExists(IFirstTimeUseNoticeSentinel sentinel) { if (sentinel == null) @@ -126,9 +148,9 @@ namespace Microsoft.DotNet.Cli _client.TrackEvent(eventName, eventProperties, eventMeasurements); _client.Flush(); } - catch (Exception) + catch (Exception e) { - Debug.Fail("Exception during TrackEventTask"); + Debug.Fail(e.ToString()); } } diff --git a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs index 14f940c07..9da815196 100644 --- a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs +++ b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs @@ -43,7 +43,7 @@ namespace Microsoft.DotNet.Cli { var sessionId = Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName); - telemetry = new Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId); + telemetry = new Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId, blockThreadInitialization: true); } public bool Enabled => telemetry.Enabled; From aa3b1b67d829f26251b14edace1cbc1588684359 Mon Sep 17 00:00:00 2001 From: William Li Date: Wed, 26 Jul 2017 16:30:03 -0700 Subject: [PATCH 08/20] refactor to remove duplication --- src/dotnet/Telemetry.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/dotnet/Telemetry.cs b/src/dotnet/Telemetry.cs index cf5437a19..0f28c449a 100644 --- a/src/dotnet/Telemetry.cs +++ b/src/dotnet/Telemetry.cs @@ -37,23 +37,7 @@ namespace Microsoft.DotNet.Cli public Telemetry(IFirstTimeUseNoticeSentinel sentinel) : this(sentinel, null) { } - public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId) - { - Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel); - - if (!Enabled) - { - return; - } - - // Store the session ID in a static field so that it can be reused - CurrentSessionId = sessionId ?? Guid.NewGuid().ToString(); - - //initialize in task to offload to parallel thread - _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry()); - } - - public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId, bool blockThreadInitialization) + public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId, bool blockThreadInitialization = false) { Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel); @@ -71,6 +55,7 @@ namespace Microsoft.DotNet.Cli } else { + //initialize in task to offload to parallel thread _trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry()); } } From 9294718d8d911580cd2a7a4833b70360e8f9e4be Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Wed, 26 Jul 2017 18:24:30 -0700 Subject: [PATCH 09/20] Update Roslyn and F# satellites --- build/DependencyVersions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index a76e5ad6b..e006cf7f3 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -4,10 +4,10 @@ 2.0.0 15.3.409 2.3.2-beta1-61921-05 - 2.3.0-pre-20170624-6 + 2.3.0-pre-20170727-1 1.6.0-beta2-25304 4.2.0-rc-170630-0 - 4.4.1-pre-20170624-6 + 4.4.1-pre-20170727-1 - + + + + $(SdkBrandName) + + + $(SdkVersion) + diff --git a/build/package/Installer.PKG.targets b/build/package/Installer.PKG.targets index 53a794905..c2c3f8aec 100644 --- a/build/package/Installer.PKG.targets +++ b/build/package/Installer.PKG.targets @@ -14,6 +14,9 @@ $(RepoRoot)/packaging/osx/clisdk $(SdkPkgSourcesRootDirectory)/scripts + $(SdkPkgScriptsDirectory)/postinstall + $(PkgIntermediateDirectory)/scripts + $(SdkPkgDestinationScriptsDirectory)/postinstall $(SdkPkgSourcesRootDirectory)/resources $(SdkPkgSourcesRootDirectory)/Distribution-Template @@ -54,6 +57,10 @@ $(HostFxrBrandName) + + + $(SdkVersion) + @@ -83,12 +90,22 @@ + + + + diff --git a/build/package/Installer.RPM.props b/build/package/Installer.RPM.props index d868c70e0..1b93101c3 100644 --- a/build/package/Installer.RPM.props +++ b/build/package/Installer.RPM.props @@ -4,5 +4,6 @@ rpm_config.json $(RepoRoot)/packaging/rpm/templates $(RepoRoot)/packaging/rpm/scripts + after_install_host.sh \ No newline at end of file diff --git a/build/package/Installer.RPM.targets b/build/package/Installer.RPM.targets index 13818cd91..f152fb43a 100644 --- a/build/package/Installer.RPM.targets +++ b/build/package/Installer.RPM.targets @@ -55,6 +55,8 @@ $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp) $(AspNetCoreVersion)-$(AspNetCoreRelease)-$(AspNetCoreRuntimePackageTimestamp) $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeRpmInstallerFileName).rpm + $(ScriptsDir)/$(AfterInstallHostScriptName) + $(RpmLayoutScripts)$(AfterInstallHostScriptName) @@ -78,7 +80,7 @@ - + $(SdkRpmPackageName) + + + $(SdkVersion) + @@ -135,6 +141,14 @@ + + + + diff --git a/packaging/deb/postinst b/packaging/deb/postinst index e5ca8ee43..be907e7dd 100755 --- a/packaging/deb/postinst +++ b/packaging/deb/postinst @@ -19,4 +19,4 @@ Installation Note -------------- A command will be run during the install process that will improve project restore speed and enable offline access. It will take up to a minute to complete." -dotnet internal-reportinstallsuccess "debianpackage" > /dev/null 2>&1 || true +/usr/share/dotnet/dotnet exec /usr/share/dotnet/sdk/%SDK_VERSION%/dotnet.dll internal-reportinstallsuccess "debianpackage" > /dev/null 2>&1 || true diff --git a/packaging/osx/clisdk/scripts/postinstall b/packaging/osx/clisdk/scripts/postinstall index 1ec0bf6fa..97821dea5 100755 --- a/packaging/osx/clisdk/scripts/postinstall +++ b/packaging/osx/clisdk/scripts/postinstall @@ -11,6 +11,6 @@ INSTALL_DESTINATION=$2 # A temporary fix for the permissions issue(s) chmod -R 755 $INSTALL_DESTINATION -$INSTALL_DESTINATION/dotnet internal-reportinstallsuccess "$1" > /dev/null 2>&1 || true +$INSTALL_DESTINATION/dotnet exec $INSTALL_DESTINATION/sdk/%SDK_VERSION%/dotnet.dll internal-reportinstallsuccess "$1" > /dev/null 2>&1 || true exit 0 diff --git a/packaging/rpm/scripts/after_install_host.sh b/packaging/rpm/scripts/after_install_host.sh index 72980c801..b745998a4 100644 --- a/packaging/rpm/scripts/after_install_host.sh +++ b/packaging/rpm/scripts/after_install_host.sh @@ -23,4 +23,4 @@ Installation Note -------------- A command will be run during the install process that will improve project restore speed and enable offline access. It will take up to a minute to complete." -dotnet internal-reportinstallsuccess "rpmpackage" > /dev/null 2>&1 || true +/usr/share/dotnet/dotnet exec /usr/share/dotnet/sdk/%SDK_VERSION%/dotnet.dll internal-reportinstallsuccess "rpmpackage" > /dev/null 2>&1 || true diff --git a/packaging/windows/clisdk/dotnet.wxs b/packaging/windows/clisdk/dotnet.wxs index 315b801e5..fbcebfbbf 100644 --- a/packaging/windows/clisdk/dotnet.wxs +++ b/packaging/windows/clisdk/dotnet.wxs @@ -20,6 +20,7 @@ + @@ -32,7 +33,7 @@ Date: Thu, 27 Jul 2017 15:16:29 +0100 Subject: [PATCH 11/20] Update SPA templates to 1.0.417 --- build/DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index e006cf7f3..d62d1e9b4 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -30,7 +30,7 @@ 0.1.1-alpha-167 1.2.1-alpha-002133 0.2.0 - 1.0.0-preview-000409 + 1.0.417 0.2.0-beta-000042 From f08bdbdf8986fdbe5347c4ad37a86379cfdad52b Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Thu, 27 Jul 2017 15:16:52 +0100 Subject: [PATCH 12/20] Remove template test workaround because it's no longer needed --- .../dotnet-new.Tests/GivenThatIWantANewAppWithSpecifiedType.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/dotnet-new.Tests/GivenThatIWantANewAppWithSpecifiedType.cs b/test/dotnet-new.Tests/GivenThatIWantANewAppWithSpecifiedType.cs index e65f9def6..96bb47c51 100644 --- a/test/dotnet-new.Tests/GivenThatIWantANewAppWithSpecifiedType.cs +++ b/test/dotnet-new.Tests/GivenThatIWantANewAppWithSpecifiedType.cs @@ -42,8 +42,7 @@ namespace Microsoft.DotNet.New.Tests bool skipSpaWebpackSteps) { string rootPath = TestAssets.CreateTestDirectory(identifier: $"{language}_{projectType}").FullName; - //This works around the SPA templates not currently supporting the "--no-restore" switch - string noRestoreDirective = skipSpaWebpackSteps ? "" : "--no-restore"; + string noRestoreDirective = "--no-restore"; new TestCommand("dotnet") { WorkingDirectory = rootPath } .Execute($"new {projectType} -lang {language} -o {rootPath} --debug:ephemeral-hive {noRestoreDirective}") From 676fe41acaf143f17507ff43cc7fecd4df7f1d6b Mon Sep 17 00:00:00 2001 From: William Li Date: Wed, 26 Jul 2017 18:54:01 -0700 Subject: [PATCH 13/20] Updating the installer to make data collection statement more obvious. --- packaging/osx/clisdk/resources/cs.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/de.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/en.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/es.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/fr.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/it.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/ja.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/ko.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/pl.lproj/conclusion.html | 8 +++++++- .../osx/clisdk/resources/pt-br.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/ru.lproj/conclusion.html | 8 +++++++- packaging/osx/clisdk/resources/tr.lproj/conclusion.html | 8 +++++++- .../osx/clisdk/resources/zh-hans.lproj/conclusion.html | 8 +++++++- .../osx/clisdk/resources/zh-hant.lproj/conclusion.html | 8 +++++++- packaging/windows/clisdk/bundle.wxl | 7 ++++--- 15 files changed, 102 insertions(+), 17 deletions(-) diff --git a/packaging/osx/clisdk/resources/cs.lproj/conclusion.html b/packaging/osx/clisdk/resources/cs.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/cs.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/cs.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/de.lproj/conclusion.html b/packaging/osx/clisdk/resources/de.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/de.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/de.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/en.lproj/conclusion.html b/packaging/osx/clisdk/resources/en.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/en.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/en.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/es.lproj/conclusion.html b/packaging/osx/clisdk/resources/es.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/es.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/es.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/fr.lproj/conclusion.html b/packaging/osx/clisdk/resources/fr.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/fr.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/fr.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/it.lproj/conclusion.html b/packaging/osx/clisdk/resources/it.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/it.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/it.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/ja.lproj/conclusion.html b/packaging/osx/clisdk/resources/ja.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/ja.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/ja.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/ko.lproj/conclusion.html b/packaging/osx/clisdk/resources/ko.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/ko.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/ko.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/pl.lproj/conclusion.html b/packaging/osx/clisdk/resources/pl.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/pl.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/pl.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/pt-br.lproj/conclusion.html b/packaging/osx/clisdk/resources/pt-br.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/pt-br.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/pt-br.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/ru.lproj/conclusion.html b/packaging/osx/clisdk/resources/ru.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/ru.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/ru.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/tr.lproj/conclusion.html b/packaging/osx/clisdk/resources/tr.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/tr.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/tr.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/zh-hans.lproj/conclusion.html b/packaging/osx/clisdk/resources/zh-hans.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/zh-hans.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/zh-hans.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/osx/clisdk/resources/zh-hant.lproj/conclusion.html b/packaging/osx/clisdk/resources/zh-hant.lproj/conclusion.html index 74addd70f..a4b936329 100644 --- a/packaging/osx/clisdk/resources/zh-hant.lproj/conclusion.html +++ b/packaging/osx/clisdk/resources/zh-hant.lproj/conclusion.html @@ -13,6 +13,13 @@
+
+

This product collects usage data +

+
+
diff --git a/packaging/windows/clisdk/bundle.wxl b/packaging/windows/clisdk/bundle.wxl index 7c93a4955..a5b12953c 100644 --- a/packaging/windows/clisdk/bundle.wxl +++ b/packaging/windows/clisdk/bundle.wxl @@ -54,13 +54,14 @@ The following were installed at [DOTNETHOME] • .NET Core Runtime 2.0.0 • Runtime Package Store -Resources +This product collects usage data + • More information and opt-out https://aka.ms/dotnet-cli-telemetry +Resources • Core Documentation https://aka.ms/dotnet-docs • SDK Documentation https://aka.ms/dotnet-cli-docs • Release Notes https://aka.ms/20-p2-rel-notes - • Tutorials https://aka.ms/dotnet-tutorials - • .NET Core Telemetry https://aka.ms/dotnet-cli-telemetry
+ • Tutorials https://aka.ms/dotnet-tutorials
.NET Core SDK .NET Core is a development platform that you can use to build command-line applications, microservices and modern websites. It is open source, cross-platform, and supported by Microsoft. We hope you enjoy it! From 081f2089421fab7149ff7f060c6cef67a6794e15 Mon Sep 17 00:00:00 2001 From: William Li Date: Mon, 5 Jun 2017 20:51:58 -0700 Subject: [PATCH 14/20] Add telemetry data points for .NET Core 2.0 --- .../ITelemetryFilter.cs | 12 + .../TelemetryEventEntry.cs | 87 +++++++ .../IUserLevelCacheWriter.cs | 12 + .../UserLevelCacheWriter.cs | 73 ++++++ src/dotnet/Program.cs | 28 ++- .../AllowListToSendFirstAppliedOptions.cs | 42 ++++ .../Telemetry/AllowListToSendFirstArgument.cs | 45 ++++ .../DockerContainerDetectorForTelemetry.cs | 58 +++++ .../Telemetry/IDockerContainerDetector.cs | 10 + src/dotnet/Telemetry/IParseResultLogRule.cs | 14 ++ src/dotnet/{ => Telemetry}/ITelemetry.cs | 3 +- src/dotnet/Telemetry/MacAddressGetter.cs | 168 +++++++++++++ src/dotnet/Telemetry/Sha256Hasher.cs | 32 +++ src/dotnet/{ => Telemetry}/Telemetry.cs | 40 ++- .../Telemetry/TelemetryCommonProperties.cs | 77 ++++++ src/dotnet/Telemetry/TelemetryFilter.cs | 95 ++++++++ .../TopLevelCommandNameAndOptionToLog.cs | 49 ++++ .../InternalReportinstallsuccessCommand.cs | 5 +- .../dotnet-msbuild/MSBuildForwardingApp.cs | 1 + .../commands/dotnet-msbuild/MSBuildLogger.cs | 7 +- .../commands/dotnet-new/NewCommandShim.cs | 1 + src/dotnet/commands/dotnet-nuget/Program.cs | 2 +- ...nAFunctionReturnStringAndFakeFileSystem.cs | 165 +++++++++++++ .../GivenDotnetMSBuildBuildsProjects.cs | 12 +- .../FakeRecordEventNameTelemetry.cs | 39 +++ ...atTheUserIsRunningDotNetForTheFirstTime.cs | 5 +- test/dotnet.Tests/TelemetryCommandTest.cs | 227 ++++++++++++++++-- .../TelemetryCommonPropertiesTests.cs | 54 +++++ .../FakeRecordEventNameTelemetry.cs | 38 +++ 29 files changed, 1324 insertions(+), 77 deletions(-) create mode 100644 src/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs create mode 100644 src/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs create mode 100644 src/Microsoft.DotNet.Configurer/IUserLevelCacheWriter.cs create mode 100644 src/Microsoft.DotNet.Configurer/UserLevelCacheWriter.cs create mode 100644 src/dotnet/Telemetry/AllowListToSendFirstAppliedOptions.cs create mode 100644 src/dotnet/Telemetry/AllowListToSendFirstArgument.cs create mode 100644 src/dotnet/Telemetry/DockerContainerDetectorForTelemetry.cs create mode 100644 src/dotnet/Telemetry/IDockerContainerDetector.cs create mode 100644 src/dotnet/Telemetry/IParseResultLogRule.cs rename src/dotnet/{ => Telemetry}/ITelemetry.cs (90%) create mode 100644 src/dotnet/Telemetry/MacAddressGetter.cs create mode 100644 src/dotnet/Telemetry/Sha256Hasher.cs rename src/dotnet/{ => Telemetry}/Telemetry.cs (76%) create mode 100644 src/dotnet/Telemetry/TelemetryCommonProperties.cs create mode 100644 src/dotnet/Telemetry/TelemetryFilter.cs create mode 100644 src/dotnet/Telemetry/TopLevelCommandNameAndOptionToLog.cs create mode 100644 test/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs create mode 100644 test/dotnet.Tests/FakeRecordEventNameTelemetry.cs create mode 100644 test/dotnet.Tests/TelemetryCommonPropertiesTests.cs create mode 100644 test/msbuild.IntegrationTests/FakeRecordEventNameTelemetry.cs diff --git a/src/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs b/src/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs new file mode 100644 index 000000000..a1a8f5a1e --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs @@ -0,0 +1,12 @@ +// 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.Collections.Generic; + +namespace Microsoft.DotNet.Cli.Utils +{ + public interface ITelemetryFilter + { + IEnumerable Filter(object o); + } +} diff --git a/src/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs b/src/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs new file mode 100644 index 000000000..fbbf9d559 --- /dev/null +++ b/src/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.DotNet.Cli.Utils +{ + public static class TelemetryEventEntry + { + public static event EventHandler EntryPosted; + public static ITelemetryFilter TelemetryFilter { get; set; } = new BlockFilter(); + + public static void TrackEvent( + string eventName = null, + IDictionary properties = null, + IDictionary measurements = null) + { + EntryPosted?.Invoke(typeof(TelemetryEventEntry), + new InstrumentationEventArgs(eventName, properties, measurements)); + } + + public static void SendFiltered(object o = null) + { + if (o == null) + { + return; + } + + foreach (ApplicationInsightsEntryFormat entry in TelemetryFilter.Filter(o)) + { + TrackEvent(entry.EventName, entry.Properties, entry.Measurements); + } + } + + public static void Subscribe(Action, + IDictionary> subscriber) + { + void Handler(object sender, InstrumentationEventArgs eventArgs) + { + subscriber(eventArgs.EventName, eventArgs.Properties, eventArgs.Measurements); + } + + EntryPosted += Handler; + } + } + + public class BlockFilter : ITelemetryFilter + { + public IEnumerable Filter(object o) + { + return new List(); + } + } + + public class InstrumentationEventArgs : EventArgs + { + internal InstrumentationEventArgs( + string eventName, + IDictionary properties, + IDictionary measurements) + { + EventName = eventName; + Properties = properties; + Measurements = measurements; + } + + public string EventName { get; } + public IDictionary Properties { get; } + public IDictionary Measurements { get; } + } + + public class ApplicationInsightsEntryFormat + { + public ApplicationInsightsEntryFormat( + string eventName = null, + IDictionary properties = null, + IDictionary measurements = null) + { + EventName = eventName; + Properties = properties; + Measurements = measurements; + } + + public string EventName { get; } + public IDictionary Properties { get; } + public IDictionary Measurements { get; } + } +} diff --git a/src/Microsoft.DotNet.Configurer/IUserLevelCacheWriter.cs b/src/Microsoft.DotNet.Configurer/IUserLevelCacheWriter.cs new file mode 100644 index 000000000..c41b55920 --- /dev/null +++ b/src/Microsoft.DotNet.Configurer/IUserLevelCacheWriter.cs @@ -0,0 +1,12 @@ +// 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 IUserLevelCacheWriter + { + string RunWithCache(string cacheKey, Func getValueToCache); + } +} diff --git a/src/Microsoft.DotNet.Configurer/UserLevelCacheWriter.cs b/src/Microsoft.DotNet.Configurer/UserLevelCacheWriter.cs new file mode 100644 index 000000000..9e5fd4a78 --- /dev/null +++ b/src/Microsoft.DotNet.Configurer/UserLevelCacheWriter.cs @@ -0,0 +1,73 @@ +// 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; +using System.IO; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.Extensions.EnvironmentAbstractions; + +namespace Microsoft.DotNet.Configurer +{ + public class UserLevelCacheWriter : IUserLevelCacheWriter + { + private readonly IFile _file; + private readonly IDirectory _directory; + private string _dotnetUserProfileFolderPath; + + public UserLevelCacheWriter(CliFallbackFolderPathCalculator cliFallbackFolderPathCalculator) : + this( + cliFallbackFolderPathCalculator.DotnetUserProfileFolderPath, + FileSystemWrapper.Default.File, + FileSystemWrapper.Default.Directory) + { + } + + public string RunWithCache(string cacheKey, Func getValueToCache) + { + var cacheFilepath = GetCacheFilePath(cacheKey); + try + { + if (!_file.Exists(cacheFilepath)) + { + if (!_directory.Exists(_dotnetUserProfileFolderPath)) + { + _directory.CreateDirectory(_dotnetUserProfileFolderPath); + } + + var runResult = getValueToCache(); + + _file.WriteAllText(cacheFilepath, runResult); + return runResult; + } + else + { + return _file.ReadAllText(cacheFilepath); + } + } + catch (Exception ex) + { + if (ex is UnauthorizedAccessException + || ex is PathTooLongException + || ex is IOException) + { + return getValueToCache(); + } + + throw; + } + + } + + internal UserLevelCacheWriter(string dotnetUserProfileFolderPath, IFile file, IDirectory directory) + { + _file = file; + _directory = directory; + _dotnetUserProfileFolderPath = dotnetUserProfileFolderPath; + } + + private string GetCacheFilePath(string cacheKey) + { + return Path.Combine(_dotnetUserProfileFolderPath, $"{Product.Version}_{cacheKey}.dotnetUserLevelCache"); + } + } +} diff --git a/src/dotnet/Program.cs b/src/dotnet/Program.cs index 5f44524d4..6a929f4ce 100644 --- a/src/dotnet/Program.cs +++ b/src/dotnet/Program.cs @@ -2,9 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Telemetry; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Configurer; using Microsoft.DotNet.PlatformAbstractions; @@ -43,8 +45,8 @@ namespace Microsoft.DotNet.Cli } catch (Exception e) when (e.ShouldBeDisplayedAsError()) { - Reporter.Error.WriteLine(CommandContext.IsVerbose() - ? e.ToString().Red().Bold() + Reporter.Error.WriteLine(CommandContext.IsVerbose() + ? e.ToString().Red().Bold() : e.Message.Red().Bold()); var commandParsingException = e as CommandParsingException; @@ -101,9 +103,9 @@ namespace Microsoft.DotNet.Cli PrintInfo(); return 0; } - else if (IsArg(args[lastArg], "h", "help") || - args[lastArg] == "-?" || - args[lastArg] == "/?") + else if (IsArg(args[lastArg], "h", "help") || + args[lastArg] == "-?" || + args[lastArg] == "/?") { HelpCommand.PrintHelp(); return 0; @@ -139,11 +141,16 @@ namespace Microsoft.DotNet.Cli if (telemetryClient == null) { - telemetryClient = new Telemetry(firstTimeUseNoticeSentinel); + telemetryClient = new Telemetry.Telemetry(firstTimeUseNoticeSentinel); } + TelemetryEventEntry.Subscribe(telemetryClient.TrackEvent); + TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(); } - var appArgs = (lastArg + 1) >= args.Length ? Enumerable.Empty() : args.Skip(lastArg + 1).ToArray(); + IEnumerable appArgs = + (lastArg + 1) >= args.Length + ? Enumerable.Empty() + : args.Skip(lastArg + 1).ToArray(); if (verbose.HasValue) { @@ -156,12 +163,12 @@ namespace Microsoft.DotNet.Cli command = "help"; } - telemetryClient.TrackEvent(command, null, null); + TelemetryEventEntry.TrackEvent(command, null, null); int exitCode; - BuiltInCommandMetadata builtIn; - if (BuiltInCommandsCatalog.Commands.TryGetValue(command, out builtIn)) + if (BuiltInCommandsCatalog.Commands.TryGetValue(command, out var builtIn)) { + TelemetryEventEntry.SendFiltered(Parser.Instance.ParseFrom($"dotnet {command}", appArgs.ToArray())); exitCode = builtIn.Command(appArgs.ToArray()); } else @@ -173,7 +180,6 @@ namespace Microsoft.DotNet.Cli .Execute(); exitCode = result.ExitCode; } - return exitCode; } diff --git a/src/dotnet/Telemetry/AllowListToSendFirstAppliedOptions.cs b/src/dotnet/Telemetry/AllowListToSendFirstAppliedOptions.cs new file mode 100644 index 000000000..5670f6687 --- /dev/null +++ b/src/dotnet/Telemetry/AllowListToSendFirstAppliedOptions.cs @@ -0,0 +1,42 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal class AllowListToSendFirstAppliedOptions : IParseResultLogRule + { + public AllowListToSendFirstAppliedOptions( + HashSet topLevelCommandNameAllowList) + { + _topLevelCommandNameAllowList = topLevelCommandNameAllowList; + } + + private HashSet _topLevelCommandNameAllowList { get; } + + public List AllowList(ParseResult parseResult) + { + var topLevelCommandNameFromParse = parseResult["dotnet"]?.AppliedOptions?.FirstOrDefault()?.Name; + var result = new List(); + if (_topLevelCommandNameAllowList.Contains(topLevelCommandNameFromParse)) + { + var firstOption = parseResult["dotnet"]?[topLevelCommandNameFromParse] + ?.AppliedOptions?.FirstOrDefault()?.Name; + if (firstOption != null) + { + result.Add(new ApplicationInsightsEntryFormat( + "dotnet-" + topLevelCommandNameFromParse, + new Dictionary + { + {"argument", firstOption} + })); + } + } + return result; + } + } +} diff --git a/src/dotnet/Telemetry/AllowListToSendFirstArgument.cs b/src/dotnet/Telemetry/AllowListToSendFirstArgument.cs new file mode 100644 index 000000000..b18c6e4f4 --- /dev/null +++ b/src/dotnet/Telemetry/AllowListToSendFirstArgument.cs @@ -0,0 +1,45 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal class AllowListToSendFirstArgument : IParseResultLogRule + { + public AllowListToSendFirstArgument( + HashSet topLevelCommandNameAllowList) + { + _topLevelCommandNameAllowList = topLevelCommandNameAllowList; + } + + private HashSet _topLevelCommandNameAllowList { get; } + + public List AllowList(ParseResult parseResult) + { + var result = new List(); + var topLevelCommandNameFromParse = parseResult["dotnet"]?.AppliedOptions?.FirstOrDefault()?.Name; + if (topLevelCommandNameFromParse != null) + { + if (_topLevelCommandNameAllowList.Contains(topLevelCommandNameFromParse)) + { + var firstArgument = parseResult["dotnet"][topLevelCommandNameFromParse].Arguments + ?.FirstOrDefault(); + if (firstArgument != null) + { + result.Add(new ApplicationInsightsEntryFormat( + "dotnet-" + topLevelCommandNameFromParse, + new Dictionary + { + {"argument", firstArgument} + })); + } + } + } + return result; + } + } +} diff --git a/src/dotnet/Telemetry/DockerContainerDetectorForTelemetry.cs b/src/dotnet/Telemetry/DockerContainerDetectorForTelemetry.cs new file mode 100644 index 000000000..b06250f75 --- /dev/null +++ b/src/dotnet/Telemetry/DockerContainerDetectorForTelemetry.cs @@ -0,0 +1,58 @@ +// 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 System.Security; +using Microsoft.Win32; +using Microsoft.DotNet.PlatformAbstractions; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal class DockerContainerDetectorForTelemetry : IDockerContainerDetector + { + public IsDockerContainer IsDockerContainer() + { + switch (RuntimeEnvironment.OperatingSystemPlatform) + { + case Platform.Windows: + try + { + using (RegistryKey subkey + = Registry.LocalMachine.OpenSubKey("System\\CurrentControlSet\\Control")) + { + return subkey?.GetValue("ContainerType") != null + ? Cli.Telemetry.IsDockerContainer.True + : Cli.Telemetry.IsDockerContainer.False; + } + } + catch (SecurityException) + { + return Cli.Telemetry.IsDockerContainer.Unknown; + } + case Platform.Linux: + return ReadProcToDetectDockerInLinux() + ? Cli.Telemetry.IsDockerContainer.True + : Cli.Telemetry.IsDockerContainer.False; + case Platform.Unknown: + return Cli.Telemetry.IsDockerContainer.Unknown; + case Platform.Darwin: + default: + return Cli.Telemetry.IsDockerContainer.False; + } + } + + private static bool ReadProcToDetectDockerInLinux() + { + return File + .ReadAllText("/proc/1/cgroup") + .Contains("/docker/"); + } + } + + internal enum IsDockerContainer + { + True, + False, + Unknown + } +} diff --git a/src/dotnet/Telemetry/IDockerContainerDetector.cs b/src/dotnet/Telemetry/IDockerContainerDetector.cs new file mode 100644 index 000000000..49bb8282c --- /dev/null +++ b/src/dotnet/Telemetry/IDockerContainerDetector.cs @@ -0,0 +1,10 @@ +// 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.Telemetry +{ + internal interface IDockerContainerDetector + { + IsDockerContainer IsDockerContainer(); + } +} diff --git a/src/dotnet/Telemetry/IParseResultLogRule.cs b/src/dotnet/Telemetry/IParseResultLogRule.cs new file mode 100644 index 000000000..8120e772a --- /dev/null +++ b/src/dotnet/Telemetry/IParseResultLogRule.cs @@ -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.Collections.Generic; +using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal interface IParseResultLogRule + { + List AllowList(ParseResult parseResult); + } +} diff --git a/src/dotnet/ITelemetry.cs b/src/dotnet/Telemetry/ITelemetry.cs similarity index 90% rename from src/dotnet/ITelemetry.cs rename to src/dotnet/Telemetry/ITelemetry.cs index e687d13bd..8079398ca 100644 --- a/src/dotnet/ITelemetry.cs +++ b/src/dotnet/Telemetry/ITelemetry.cs @@ -1,10 +1,9 @@ // 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; using System.Collections.Generic; -namespace Microsoft.DotNet.Cli +namespace Microsoft.DotNet.Cli.Telemetry { public interface ITelemetry { diff --git a/src/dotnet/Telemetry/MacAddressGetter.cs b/src/dotnet/Telemetry/MacAddressGetter.cs new file mode 100644 index 000000000..e9ef66ba9 --- /dev/null +++ b/src/dotnet/Telemetry/MacAddressGetter.cs @@ -0,0 +1,168 @@ +// 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; +using System.Linq; +using System.Diagnostics; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; +using System.Net.NetworkInformation; +using System.ComponentModel; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal static class MacAddressGetter + { + private const string MacRegex = @"(?:[a-z0-9]{2}[:\-]){5}[a-z0-9]{2}"; + private const string ZeroRegex = @"(?:00[:\-]){5}00"; + private const int ErrorFileNotFound = 0x2; + public static string GetMacAddress() + { + try + { + var shelloutput = GetShellOutMacAddressOutput(); + if (shelloutput == null) + { + return null; + } + + return ParseMACAddress(shelloutput); + } + catch (Win32Exception e) + { + if (e.NativeErrorCode == ErrorFileNotFound) + { + return GetMacAddressByNetworkInterface(); + } + else + { + throw; + } + } + } + + private static string ParseMACAddress(string shelloutput) + { + string macAddress = null; + foreach (Match match in Regex.Matches(shelloutput, MacRegex, RegexOptions.IgnoreCase)) + { + if (!Regex.IsMatch(match.Value, ZeroRegex)) + { + macAddress = match.Value; + break; + } + } + + if (macAddress != null) + { + return macAddress; + } + return null; + } + + private static string GetIpCommandOutput() + { + var ipResult = new ProcessStartInfo + { + FileName = "ip", + Arguments = "link", + UseShellExecute = false + }.ExecuteAndCaptureOutput(out string ipStdOut, out string ipStdErr); + + if (ipResult == 0) + { + return ipStdOut; + } + else + { + return null; + } + } + + private static string GetShellOutMacAddressOutput() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var result = new ProcessStartInfo + { + FileName = "getmac.exe", + UseShellExecute = false + }.ExecuteAndCaptureOutput(out string stdOut, out string stdErr); + + if (result == 0) + { + return stdOut; + } + else + { + return null; + } + } + else + { + try + { + var ifconfigResult = new ProcessStartInfo + { + FileName = "ifconfig", + Arguments = "-a", + UseShellExecute = false + }.ExecuteAndCaptureOutput(out string ifconfigStdOut, out string ifconfigStdErr); + + if (ifconfigResult == 0) + { + return ifconfigStdOut; + } + else + { + return GetIpCommandOutput(); + } + } + catch (Win32Exception e) + { + if (e.NativeErrorCode == ErrorFileNotFound) + { + return GetIpCommandOutput(); + } + else + { + throw; + } + } + } + } + + private static string GetMacAddressByNetworkInterface() + { + return GetMacAddressesByNetworkInterface().FirstOrDefault(); + } + + private static List GetMacAddressesByNetworkInterface() + { + NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); + var macs = new List(); + + if (nics == null || nics.Length < 1) + { + macs.Add(string.Empty); + return macs; + } + + foreach (NetworkInterface adapter in nics) + { + IPInterfaceProperties properties = adapter.GetIPProperties(); + + PhysicalAddress address = adapter.GetPhysicalAddress(); + byte[] bytes = address.GetAddressBytes(); + macs.Add(string.Join("-", bytes.Select(x => x.ToString("X2")))); + if (macs.Count >= 10) + { + break; + } + } + return macs; + } + } +} diff --git a/src/dotnet/Telemetry/Sha256Hasher.cs b/src/dotnet/Telemetry/Sha256Hasher.cs new file mode 100644 index 000000000..bbe7cfdfb --- /dev/null +++ b/src/dotnet/Telemetry/Sha256Hasher.cs @@ -0,0 +1,32 @@ +// 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.Collections.Generic; +using System.Security.Cryptography; +using System.Text; +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal static class Sha256Hasher + { + /// + /// // The hashed mac address needs to be the same hashed value as produced by the other distinct sources given the same input. (e.g. VsCode) + /// + public static string Hash(string text) + { + var sha256 = SHA256.Create(); + return HashInFormat(sha256, text); + } + + private static string HashInFormat(SHA256 sha256, string text) + { + byte[] bytes = Encoding.UTF8.GetBytes(text); + byte[] hash = sha256.ComputeHash(bytes); + StringBuilder hashString = new StringBuilder(); + foreach (byte x in hash) + { + hashString.AppendFormat("{0:x2}", x); + } + return hashString.ToString(); + } + } +} diff --git a/src/dotnet/Telemetry.cs b/src/dotnet/Telemetry/Telemetry.cs similarity index 76% rename from src/dotnet/Telemetry.cs rename to src/dotnet/Telemetry/Telemetry.cs index 0f28c449a..fe1209339 100644 --- a/src/dotnet/Telemetry.cs +++ b/src/dotnet/Telemetry/Telemetry.cs @@ -4,36 +4,28 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Threading.Tasks; using Microsoft.ApplicationInsights; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Configurer; using Microsoft.DotNet.PlatformAbstractions; -namespace Microsoft.DotNet.Cli +namespace Microsoft.DotNet.Cli.Telemetry { public class Telemetry : ITelemetry { internal static string CurrentSessionId = null; private TelemetryClient _client = null; - private Dictionary _commonProperties = null; private Dictionary _commonMeasurements = null; private Task _trackEventTask = null; 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"; - private const string OSVersion = "OS Version"; - private const string OSPlatform = "OS Platform"; - private const string RuntimeId = "Runtime Id"; - private const string ProductVersion = "Product Version"; - private const string TelemetryProfile = "Telemetry Profile"; public bool Enabled { get; } - public Telemetry () : this(null) { } + public Telemetry() : this(null) { } public Telemetry(IFirstTimeUseNoticeSentinel sentinel) : this(sentinel, null) { } @@ -70,7 +62,8 @@ namespace Microsoft.DotNet.Cli return sentinel.Exists(); } - public void TrackEvent(string eventName, IDictionary properties, IDictionary measurements) + public void TrackEvent(string eventName, IDictionary properties, + IDictionary measurements) { if (!Enabled) { @@ -99,26 +92,23 @@ namespace Microsoft.DotNet.Cli _client = new TelemetryClient(); _client.InstrumentationKey = InstrumentationKey; _client.Context.Session.Id = CurrentSessionId; - _client.Context.Device.OperatingSystem = RuntimeEnvironment.OperatingSystem; - _commonProperties = new Dictionary(); - _commonProperties.Add(OSVersion, RuntimeEnvironment.OperatingSystemVersion); - _commonProperties.Add(OSPlatform, RuntimeEnvironment.OperatingSystemPlatform.ToString()); - _commonProperties.Add(RuntimeId, RuntimeEnvironment.GetRuntimeIdentifier()); - _commonProperties.Add(ProductVersion, Product.Version); - _commonProperties.Add(TelemetryProfile, Environment.GetEnvironmentVariable(TelemetryProfileEnvironmentVariable)); + _commonProperties = new TelemetryCommonProperties().GetTelemetryCommonProperties(); _commonMeasurements = new Dictionary(); } - catch (Exception) + catch (Exception e) { _client = null; // we dont want to fail the tool if telemetry fails. - Debug.Fail("Exception during telemetry initialization"); + Debug.Fail(e.ToString()); } } - private void TrackEventTask(string eventName, IDictionary properties, IDictionary measurements) + private void TrackEventTask( + string eventName, + IDictionary properties, + IDictionary measurements) { if (_client == null) { @@ -127,8 +117,8 @@ namespace Microsoft.DotNet.Cli try { - var eventProperties = GetEventProperties(properties); - var eventMeasurements = GetEventMeasures(measurements); + Dictionary eventProperties = GetEventProperties(properties); + Dictionary eventMeasurements = GetEventMeasures(measurements); _client.TrackEvent(eventName, eventProperties, eventMeasurements); _client.Flush(); @@ -144,7 +134,7 @@ namespace Microsoft.DotNet.Cli Dictionary eventMeasurements = new Dictionary(_commonMeasurements); if (measurements != null) { - foreach (var measurement in measurements) + foreach (KeyValuePair measurement in measurements) { if (eventMeasurements.ContainsKey(measurement.Key)) { @@ -164,7 +154,7 @@ namespace Microsoft.DotNet.Cli if (properties != null) { var eventProperties = new Dictionary(_commonProperties); - foreach (var property in properties) + foreach (KeyValuePair property in properties) { if (eventProperties.ContainsKey(property.Key)) { diff --git a/src/dotnet/Telemetry/TelemetryCommonProperties.cs b/src/dotnet/Telemetry/TelemetryCommonProperties.cs new file mode 100644 index 000000000..a14c5cf84 --- /dev/null +++ b/src/dotnet/Telemetry/TelemetryCommonProperties.cs @@ -0,0 +1,77 @@ +// 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; +using System.Collections.Generic; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.PlatformAbstractions; +using System.IO; +using Microsoft.DotNet.Configurer; +using System.Linq; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal class TelemetryCommonProperties + { + public TelemetryCommonProperties( + Func getCurrentDirectory = null, + Func hasher = null, + Func getMACAddress = null, + IDockerContainerDetector dockerContainerDetector = null, + IUserLevelCacheWriter userLevelCacheWriter = null) + { + _getCurrentDirectory = getCurrentDirectory ?? Directory.GetCurrentDirectory; + _hasher = hasher ?? Sha256Hasher.Hash; + _getMACAddress = getMACAddress ?? MacAddressGetter.GetMacAddress; + _dockerContainerDetector = dockerContainerDetector ?? new DockerContainerDetectorForTelemetry(); + _userLevelCacheWriter = userLevelCacheWriter ?? new UserLevelCacheWriter(new CliFallbackFolderPathCalculator()); + } + + private readonly IDockerContainerDetector _dockerContainerDetector; + private Func _getCurrentDirectory; + private Func _hasher; + private Func _getMACAddress; + private IUserLevelCacheWriter _userLevelCacheWriter; + private const string OSVersion = "OS Version"; + private const string OSPlatform = "OS Platform"; + private const string RuntimeId = "Runtime Id"; + private const string ProductVersion = "Product Version"; + private const string TelemetryProfile = "Telemetry Profile"; + private const string CurrentPathHash = "Current Path Hash"; + private const string MachineId = "Machine ID"; + private const string DockerContainer = "Docker Container"; + private const string TelemetryProfileEnvironmentVariable = "DOTNET_CLI_TELEMETRY_PROFILE"; + private const string CannotFindMacAddress = "Unknown"; + + private const string MachineIdCacheKey = "MachineId"; + private const string IsDockerContainerCacheKey = "IsDockerContainer"; + + public Dictionary GetTelemetryCommonProperties() + { + return new Dictionary + { + {OSVersion, RuntimeEnvironment.OperatingSystemVersion}, + {OSPlatform, RuntimeEnvironment.OperatingSystemPlatform.ToString()}, + {RuntimeId, RuntimeEnvironment.GetRuntimeIdentifier()}, + {ProductVersion, Product.Version}, + {TelemetryProfile, Environment.GetEnvironmentVariable(TelemetryProfileEnvironmentVariable)}, + {DockerContainer, _userLevelCacheWriter.RunWithCache(IsDockerContainerCacheKey, () => _dockerContainerDetector.IsDockerContainer().ToString("G") )}, + {CurrentPathHash, _hasher(_getCurrentDirectory())}, + {MachineId, _userLevelCacheWriter.RunWithCache(MachineIdCacheKey, GetMachineId)} + }; + } + + private string GetMachineId() + { + var macAddress = _getMACAddress(); + if (macAddress != null) + { + return _hasher(macAddress); + } + else + { + return Guid.NewGuid().ToString(); + } + } + } +} diff --git a/src/dotnet/Telemetry/TelemetryFilter.cs b/src/dotnet/Telemetry/TelemetryFilter.cs new file mode 100644 index 000000000..6d44ef58d --- /dev/null +++ b/src/dotnet/Telemetry/TelemetryFilter.cs @@ -0,0 +1,95 @@ +// 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.Collections.Generic; +using System.Linq; +using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal class TelemetryFilter : ITelemetryFilter + { + private const string DotnetName = "dotnet"; + + public IEnumerable Filter(object objectToFilter) + { + var ruleSet = new List + { new AllowListToSendFirstArgument(new HashSet{ "new", "help" }), + new AllowListToSendFirstAppliedOptions(new HashSet{ "add", "remove", "list", "sln", "nuget" }), + new TopLevelCommandNameAndOptionToLog + ( + topLevelCommandName: new HashSet { "new" }, + optionsToLog: new HashSet { "language" } + ), + new TopLevelCommandNameAndOptionToLog + ( + topLevelCommandName: new HashSet { "build", "publish" }, + optionsToLog: new HashSet { "framework", "runtime", "configuration" } + ), + new TopLevelCommandNameAndOptionToLog + ( + topLevelCommandName: new HashSet { "run", "clean", "test" }, + optionsToLog: new HashSet { "framework", "configuration" } + ), + new TopLevelCommandNameAndOptionToLog + ( + topLevelCommandName: new HashSet { "pack" }, + optionsToLog: new HashSet { "configuration" } + ), + new TopLevelCommandNameAndOptionToLog + ( + topLevelCommandName: new HashSet { "migrate" }, + optionsToLog: new HashSet { "sdk-package-version" } + ), + new TopLevelCommandNameAndOptionToLog + ( + topLevelCommandName: new HashSet { "vstest" }, + optionsToLog: new HashSet { "platform", "framework", "logger" } + ), + new TopLevelCommandNameAndOptionToLog + ( + topLevelCommandName: new HashSet { "publish" }, + optionsToLog: new HashSet { "runtime" } + ) + }; + var result = new List(); + + if (objectToFilter is ParseResult parseResult) + { + var topLevelCommandName = parseResult[DotnetName]?.AppliedOptions?.FirstOrDefault()?.Name; + if (topLevelCommandName != null) + { + LogVerbosityForAllTopLevelCommand(result, parseResult, topLevelCommandName); + + foreach (IParseResultLogRule rule in ruleSet) + { + result.AddRange(rule.AllowList(parseResult)); + } + } + } + + return result; + } + + private static void LogVerbosityForAllTopLevelCommand( + ICollection result, + ParseResult parseResult, + string topLevelCommandName) + { + if (parseResult[DotnetName][topLevelCommandName]?.AppliedOptions != null && + parseResult[DotnetName][topLevelCommandName].AppliedOptions.Contains("verbosity")) + { + AppliedOption appliedOptions = + parseResult[DotnetName][topLevelCommandName].AppliedOptions["verbosity"]; + + result.Add(new ApplicationInsightsEntryFormat( + "dotnet-" + topLevelCommandName, + new Dictionary() + { + {"verbosity", appliedOptions.Arguments.ElementAt(0)} + })); + } + } + } +} diff --git a/src/dotnet/Telemetry/TopLevelCommandNameAndOptionToLog.cs b/src/dotnet/Telemetry/TopLevelCommandNameAndOptionToLog.cs new file mode 100644 index 000000000..38a234541 --- /dev/null +++ b/src/dotnet/Telemetry/TopLevelCommandNameAndOptionToLog.cs @@ -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.Collections.Generic; +using System.Linq; +using Microsoft.DotNet.Cli.CommandLine; +using Microsoft.DotNet.Cli.Utils; + +namespace Microsoft.DotNet.Cli.Telemetry +{ + internal class TopLevelCommandNameAndOptionToLog : IParseResultLogRule + { + public TopLevelCommandNameAndOptionToLog( + HashSet topLevelCommandName, + HashSet optionsToLog) + { + _topLevelCommandName = topLevelCommandName; + _optionsToLog = optionsToLog; + } + + private HashSet _topLevelCommandName { get; } + private HashSet _optionsToLog { get; } + private const string DotnetName = "dotnet"; + + public List AllowList(ParseResult parseResult) + { + var topLevelCommandName = parseResult[DotnetName]?.AppliedOptions?.FirstOrDefault()?.Name; + var result = new List(); + foreach (var option in _optionsToLog) + { + if (_topLevelCommandName.Contains(topLevelCommandName) + && parseResult[DotnetName]?[topLevelCommandName]?.AppliedOptions != null + && parseResult[DotnetName][topLevelCommandName].AppliedOptions.Contains(option)) + { + AppliedOption appliedOptions = + parseResult[DotnetName][topLevelCommandName] + .AppliedOptions[option]; + result.Add(new ApplicationInsightsEntryFormat( + "dotnet-" + topLevelCommandName, + new Dictionary + { + {option, appliedOptions.Arguments.ElementAt(0)} + })); + } + } + return result; + } + } +} diff --git a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs index 9da815196..b44e9b41c 100644 --- a/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs +++ b/src/dotnet/commands/dotnet-internal-reportinstallsuccess/InternalReportinstallsuccessCommand.cs @@ -6,6 +6,7 @@ using System.Linq; using System.IO; using System.Collections.Generic; using Microsoft.DotNet.Configurer; +using Microsoft.DotNet.Cli.Telemetry; namespace Microsoft.DotNet.Cli { @@ -37,13 +38,13 @@ namespace Microsoft.DotNet.Cli internal class ThreadBlockingTelemetry : ITelemetry { - private Telemetry telemetry; + private Telemetry.Telemetry telemetry; internal ThreadBlockingTelemetry() { var sessionId = Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName); - telemetry = new Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId, blockThreadInitialization: true); + telemetry = new Telemetry.Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId, blockThreadInitialization: true); } public bool Enabled => telemetry.Enabled; diff --git a/src/dotnet/commands/dotnet-msbuild/MSBuildForwardingApp.cs b/src/dotnet/commands/dotnet-msbuild/MSBuildForwardingApp.cs index dbf81dd6e..4140990d9 100644 --- a/src/dotnet/commands/dotnet-msbuild/MSBuildForwardingApp.cs +++ b/src/dotnet/commands/dotnet-msbuild/MSBuildForwardingApp.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.CommandLine; using System.Diagnostics; +using Microsoft.DotNet.Cli.Telemetry; using Microsoft.DotNet.Cli.Utils; namespace Microsoft.DotNet.Tools.MSBuild diff --git a/src/dotnet/commands/dotnet-msbuild/MSBuildLogger.cs b/src/dotnet/commands/dotnet-msbuild/MSBuildLogger.cs index 54329cb8c..ef9d7d93e 100644 --- a/src/dotnet/commands/dotnet-msbuild/MSBuildLogger.cs +++ b/src/dotnet/commands/dotnet-msbuild/MSBuildLogger.cs @@ -5,6 +5,7 @@ using System; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Telemetry; using Microsoft.DotNet.Configurer; namespace Microsoft.DotNet.Tools.MSBuild @@ -39,15 +40,13 @@ namespace Microsoft.DotNet.Tools.MSBuild { if (_telemetry != null && _telemetry.Enabled) { - IEventSource2 eventSource2 = eventSource as IEventSource2; - - if (eventSource2 != null) + if (eventSource is IEventSource2 eventSource2) { eventSource2.TelemetryLogged += OnTelemetryLogged; } } } - catch(Exception) + catch (Exception) { // Exceptions during telemetry shouldn't cause anything else to fail } diff --git a/src/dotnet/commands/dotnet-new/NewCommandShim.cs b/src/dotnet/commands/dotnet-new/NewCommandShim.cs index a743c6e7c..cf30e3ca4 100644 --- a/src/dotnet/commands/dotnet-new/NewCommandShim.cs +++ b/src/dotnet/commands/dotnet-new/NewCommandShim.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Reflection; using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Telemetry; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Configurer; using Microsoft.DotNet.Tools.MSBuild; diff --git a/src/dotnet/commands/dotnet-nuget/Program.cs b/src/dotnet/commands/dotnet-nuget/Program.cs index b22588c0d..8e6378b41 100644 --- a/src/dotnet/commands/dotnet-nuget/Program.cs +++ b/src/dotnet/commands/dotnet-nuget/Program.cs @@ -32,7 +32,7 @@ namespace Microsoft.DotNet.Tools.NuGet private class NuGetCommandRunner : ICommandRunner { - public int Run(string [] args) + public int Run(string[] args) { var nugetApp = new NuGetForwardingApp(args); diff --git a/test/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs b/test/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs new file mode 100644 index 000000000..4dfaf9c9d --- /dev/null +++ b/test/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs @@ -0,0 +1,165 @@ +// 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; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using FluentAssertions; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Configurer; +using Microsoft.Extensions.DependencyModel.Tests; +using Microsoft.Extensions.EnvironmentAbstractions; +using Moq; +using Xunit; + +namespace Microsoft.DotNet.Configurer.UnitTests +{ + public class GivenAFunctionReturnStringAndFakeFileSystem + { + private const string DOTNET_USER_PROFILE_FOLDER_PATH = "some path"; + + private FileSystemMockBuilder _fileSystemMockBuilder; + private UserLevelCacheWriter _userLevelCacheWriter; + private IFileSystem _fileSystemMock; + + public GivenAFunctionReturnStringAndFakeFileSystem() + { + _fileSystemMockBuilder = FileSystemMockBuilder.Create(); + _fileSystemMock = _fileSystemMockBuilder.Build(); + _userLevelCacheWriter = + new UserLevelCacheWriter( + DOTNET_USER_PROFILE_FOLDER_PATH, + _fileSystemMock.File, + _fileSystemMock.Directory); + } + + [Fact] + public void ItReturnsTheFunctionResult() + { + _userLevelCacheWriter.RunWithCache("fooKey", () => "foo").Should().Be("foo"); + } + + [Fact] + public void ItRunsTheFunctionOnlyOnceWhenInvokeTwice() + { + var counter = new Counter(); + Func func = () => + { + counter.Increase(); + return "foo"; + }; + + _userLevelCacheWriter.RunWithCache("fookey", func).Should().Be("foo"); + _userLevelCacheWriter.RunWithCache("fookey", func).Should().Be("foo"); + counter.Count.Should().Be(1); + } + + [Fact] + public void ItKeepsTheCacheInUserProfileWithCacheKey() + { + _userLevelCacheWriter.RunWithCache("fooKey", () => "foo"); + var path = Path.Combine("some path", $"{Product.Version}_fooKey.dotnetUserLevelCache"); + _fileSystemMock.File.Exists(path); + _fileSystemMock.File.ReadAllText(path).Should().Be("foo"); + } + + [Fact] + public void ItRunsAndReturnsTheValueIfCacheCreationFailed() + { + var mockFile = new Mock(); + + var systemUndertest = + new UserLevelCacheWriter( + DOTNET_USER_PROFILE_FOLDER_PATH, + new NoPermissionFileFake(), + new NoPermissionDirectoryFake()); + + var counter = new Counter(); + Func func = () => + { + counter.Increase(); + return "foo"; + }; + + systemUndertest.RunWithCache("fookey", func).Should().Be("foo"); + systemUndertest.RunWithCache("fookey", func).Should().Be("foo"); + counter.Count.Should().Be(2); + } + + private class NoPermissionFileFake : IFile + { + public bool Exists(string path) + { + return false; + } + + public string ReadAllText(string path) + { + throw new UnauthorizedAccessException(); + } + + public Stream OpenRead(string path) + { + throw new UnauthorizedAccessException(); + } + + public Stream OpenFile( + string path, + FileMode fileMode, + FileAccess fileAccess, + FileShare fileShare, + int bufferSize, + FileOptions fileOptions) + { + throw new NotImplementedException(); + } + + public void CreateEmptyFile(string path) + { + throw new UnauthorizedAccessException(); + } + + public void WriteAllText(string path, string content) + { + throw new UnauthorizedAccessException(); + } + } + + private class NoPermissionDirectoryFake : IDirectory + { + + public ITemporaryDirectory CreateTemporaryDirectory() + { + throw new NotImplementedException(); + } + + public IEnumerable GetFiles(string path, string searchPattern) + { + throw new UnauthorizedAccessException(); + } + + public string GetDirectoryFullName(string path) + { + throw new NotImplementedException(); + } + + public bool Exists(string path) + { + return false; + } + + public void CreateDirectory(string path) + { + throw new UnauthorizedAccessException(); + } + } + + private class Counter + { + public int Count { get; private set; } + public void Increase() { Count++; } + + } + } +} diff --git a/test/dotnet-msbuild.Tests/GivenDotnetMSBuildBuildsProjects.cs b/test/dotnet-msbuild.Tests/GivenDotnetMSBuildBuildsProjects.cs index e05eead68..52d776505 100644 --- a/test/dotnet-msbuild.Tests/GivenDotnetMSBuildBuildsProjects.cs +++ b/test/dotnet-msbuild.Tests/GivenDotnetMSBuildBuildsProjects.cs @@ -18,6 +18,7 @@ using MSBuildCommand = Microsoft.DotNet.Tools.Test.Utilities.MSBuildCommand; using System.Diagnostics; using System.Threading; + // There are tests which modify static Telemetry.CurrentSessionId and they cannot run in parallel [assembly: CollectionBehavior(DisableTestParallelization = true)] @@ -129,7 +130,7 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests [Fact] public void WhenTelemetryIsEnabledTheLoggerIsAddedToTheCommandLine() { - Telemetry telemetry; + Telemetry.Telemetry telemetry; string[] allArgs = GetArgsForMSBuild(() => true, out telemetry); // telemetry will still be disabled if environment variable is set if (telemetry.Enabled) @@ -156,14 +157,15 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests private string[] GetArgsForMSBuild(Func sentinelExists) { - Telemetry telemetry; + Telemetry.Telemetry telemetry; return GetArgsForMSBuild(sentinelExists, out telemetry); } - private string[] GetArgsForMSBuild(Func sentinelExists, out Telemetry telemetry) + private string[] GetArgsForMSBuild(Func sentinelExists, out Telemetry.Telemetry telemetry) { - Telemetry.CurrentSessionId = null; // reset static session id modified by telemetry constructor - telemetry = new Telemetry(new MockNuGetCacheSentinel(sentinelExists)); + + Telemetry.Telemetry.CurrentSessionId = null; // reset static session id modified by telemetry constructor + telemetry = new Telemetry.Telemetry(new MockNuGetCacheSentinel(sentinelExists)); MSBuildForwardingApp msBuildForwardingApp = new MSBuildForwardingApp(Enumerable.Empty()); diff --git a/test/dotnet.Tests/FakeRecordEventNameTelemetry.cs b/test/dotnet.Tests/FakeRecordEventNameTelemetry.cs new file mode 100644 index 000000000..4690ef771 --- /dev/null +++ b/test/dotnet.Tests/FakeRecordEventNameTelemetry.cs @@ -0,0 +1,39 @@ +// 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.Collections.Concurrent; +using System.Collections.Generic; +using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Telemetry; + +namespace Microsoft.DotNet.Tests +{ + public class FakeRecordEventNameTelemetry : ITelemetry + { + public bool Enabled { get; set; } + + public string EventName { get; set; } + + public void TrackEvent(string eventName, + IDictionary properties, + IDictionary measurements) + { + LogEntries.Add( + new LogEntry + { + EventName = eventName, + Measurement = measurements, + Properties = properties + }); + } + + public ConcurrentBag LogEntries { get; set; } = new ConcurrentBag(); + + public class LogEntry + { + public string EventName { get; set; } + public IDictionary Properties { get; set; } + public IDictionary Measurement { get; set; } + } + } +} diff --git a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs index f323a7d8d..4fd1fb660 100644 --- a/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs +++ b/test/dotnet.Tests/GivenThatTheUserIsRunningDotNetForTheFirstTime.cs @@ -106,8 +106,9 @@ namespace Microsoft.DotNet.Tests command.ExecuteWithCapturedOutput("internal-reportinstallsuccess test").Should().Pass(); - var emptyHomeFolder = new DirectoryInfo(Path.Combine(emptyHome, ".dotnet")); - emptyHomeFolder.Should().NotExist(); + var homeFolder = new DirectoryInfo(Path.Combine(emptyHome, ".dotnet")); + string[] fileEntries = Directory.GetFiles(homeFolder.ToString()); + fileEntries.Should().OnlyContain(x => !x.Contains(".dotnetFirstUseSentinel")); } [Fact] diff --git a/test/dotnet.Tests/TelemetryCommandTest.cs b/test/dotnet.Tests/TelemetryCommandTest.cs index 731713a59..356667009 100644 --- a/test/dotnet.Tests/TelemetryCommandTest.cs +++ b/test/dotnet.Tests/TelemetryCommandTest.cs @@ -1,52 +1,229 @@ // 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; -using System.IO; -using System.Collections.Generic; -using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Cli; -using Microsoft.DotNet.Tools.Test.Utilities; -using Xunit; using FluentAssertions; +using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Telemetry; +using Microsoft.DotNet.Cli.Utils; +using Microsoft.DotNet.Tools.Test.Utilities; +using System.Collections.Generic; +using Xunit; namespace Microsoft.DotNet.Tests { - public class MockTelemetry : ITelemetry + public class TelemetryCommandTests : TestBase { - public bool Enabled { get; set; } + private readonly FakeRecordEventNameTelemetry _fakeTelemetry; public string EventName { get; set; } public IDictionary Properties { get; set; } - - public void TrackEvent(string eventName, IDictionary properties, IDictionary measurements) + public TelemetryCommandTests() { - EventName = eventName; - Properties = properties; + _fakeTelemetry = new FakeRecordEventNameTelemetry(); + TelemetryEventEntry.Subscribe(_fakeTelemetry.TrackEvent); + TelemetryEventEntry.TelemetryFilter = new TelemetryFilter(); } - } - public class TelemetryCommandTests : TestBase - { [Fact] - public void TestProjectDependencyIsNotAvailableThroughDriver() + public void TopLevelCommandNameShouldBeSentToTelemetry() { - MockTelemetry mockTelemetry = new MockTelemetry(); - string[] args = { "help" }; - Microsoft.DotNet.Cli.Program.ProcessArgs(args, mockTelemetry); - Assert.Equal(mockTelemetry.EventName, args[0]); + string[] args = {"help"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry.LogEntries.Should().Contain(e => e.EventName == args[0]); + } + + [Fact] + public void DotnetNewCommandFirstArgumentShouldBeSentToTelemetry() + { + const string argumentToSend = "console"; + string[] args = {"new", argumentToSend}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-new" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetHelpCommandFirstArgumentShouldBeSentToTelemetry() + { + const string argumentToSend = "something"; + string[] args = {"help", argumentToSend}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-help" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry() + { + const string argumentToSend = "package"; + string[] args = {"add", argumentToSend, "aPackageName"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-add" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetAddCommandFirstArgumentShouldBeSentToTelemetry2() + { + const string argumentToSend = "reference"; + string[] args = {"add", argumentToSend, "aPackageName"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-add" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetRemoveCommandFirstArgumentShouldBeSentToTelemetry() + { + const string argumentToSend = "package"; + string[] args = {"remove", argumentToSend, "aPackageName"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-remove" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetListCommandFirstArgumentShouldBeSentToTelemetry() + { + const string argumentToSend = "reference"; + string[] args = {"list", argumentToSend, "aPackageName"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-list" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetSlnCommandFirstArgumentShouldBeSentToTelemetry() + { + const string argumentToSend = "list"; + string[] args = {"sln", "aSolution", argumentToSend}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-sln" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetNugetCommandFirstArgumentShouldBeSentToTelemetry() + { + const string argumentToSend = "push"; + string[] args = {"nuget", argumentToSend, "aRoot"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-nuget" && e.Properties.ContainsKey("argument") && + e.Properties["argument"] == argumentToSend); + } + + [Fact] + public void DotnetNewCommandLanguageOpinionShouldBeSentToTelemetry() + { + const string optionKey = "language"; + const string optionValueToSend = "c#"; + string[] args = {"new", "console", "--" + optionKey, optionValueToSend}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-new" && e.Properties.ContainsKey(optionKey) && + e.Properties[optionKey] == optionValueToSend); + } + + [Fact] + public void AnyDotnetCommandVerbosityOpinionShouldBeSentToTelemetry() + { + const string optionKey = "verbosity"; + const string optionValueToSend = "minimal"; + string[] args = {"restore", "--" + optionKey, optionValueToSend}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-restore" && e.Properties.ContainsKey(optionKey) && + e.Properties[optionKey] == optionValueToSend); + } + + [Fact] + public void DotnetBuildAndPublishCommandOpinionsShouldBeSentToTelemetry() + { + const string optionKey = "configuration"; + const string optionValueToSend = "Debug"; + string[] args = {"build", "--" + optionKey, optionValueToSend}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey(optionKey) && + e.Properties[optionKey] == optionValueToSend); + } + + [Fact] + public void DotnetPublishCommandRuntimeOpinionsShouldBeSentToTelemetry() + { + const string optionKey = "runtime"; + const string optionValueToSend = "win10-x64"; + string[] args = { "publish", "--" + optionKey, optionValueToSend }; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-publish" && e.Properties.ContainsKey(optionKey) && + e.Properties[optionKey] == optionValueToSend); + } + + [Fact] + public void DotnetBuildAndPublishCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption() + { + string[] args = {"build", "--configuration", "Debug", "--runtime", "osx.10.11-x64"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey("configuration") && + e.Properties["configuration"] == "Debug"); + + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-build" && e.Properties.ContainsKey("runtime") && + e.Properties["runtime"] == "osx.10.11-x64"); + } + + [Fact] + public void DotnetRunCleanTestCommandOpinionsShouldBeSentToTelemetryWhenThereIsMultipleOption() + { + string[] args = {"clean", "--configuration", "Debug", "--framework", "netcoreapp1.0"}; + Cli.Program.ProcessArgs(args); + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-clean" && e.Properties.ContainsKey("configuration") && + e.Properties["configuration"] == "Debug"); + + _fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "dotnet-clean" && e.Properties.ContainsKey("framework") && + e.Properties["framework"] == "netcoreapp1.0"); } [WindowsOnlyFact] public void InternalreportinstallsuccessCommandCollectExeNameWithEventname() { - MockTelemetry mockTelemetry = new MockTelemetry(); + FakeRecordEventNameTelemetry fakeTelemetry = new FakeRecordEventNameTelemetry(); string[] args = { "c:\\mypath\\dotnet-sdk-latest-win-x64.exe" }; - InternalReportinstallsuccess.ProcessInputAndSendTelemetry(args, mockTelemetry); + InternalReportinstallsuccess.ProcessInputAndSendTelemetry(args, fakeTelemetry); - mockTelemetry.EventName.Should().Be("reportinstallsuccess"); - mockTelemetry.Properties["exeName"].Should().Be("dotnet-sdk-latest-win-x64.exe"); + fakeTelemetry + .LogEntries.Should() + .Contain(e => e.EventName == "reportinstallsuccess" && e.Properties.ContainsKey("exeName") && + e.Properties["exeName"] == "dotnet-sdk-latest-win-x64.exe"); } [Fact] diff --git a/test/dotnet.Tests/TelemetryCommonPropertiesTests.cs b/test/dotnet.Tests/TelemetryCommonPropertiesTests.cs new file mode 100644 index 000000000..b30e59e9d --- /dev/null +++ b/test/dotnet.Tests/TelemetryCommonPropertiesTests.cs @@ -0,0 +1,54 @@ +// 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 FluentAssertions; +using Microsoft.DotNet.Tools.Test.Utilities; +using Xunit; +using System; +using Microsoft.DotNet.Cli; +using Microsoft.DotNet.Cli.Telemetry; +using Microsoft.DotNet.Configurer; + +namespace Microsoft.DotNet.Tests +{ + public class TelemetryCommonPropertiesTests : TestBase + { + [Fact] + public void TelemetryCommonPropertiesShouldContainIfItIsInDockerOrNot() + { + var unitUnderTest = new TelemetryCommonProperties(userLevelCacheWriter: new NothingCache()); + unitUnderTest.GetTelemetryCommonProperties().Should().ContainKey("Docker Container"); + } + + [Fact] + public void TelemetryCommonPropertiesShouldReturnHashedPath() + { + var unitUnderTest = new TelemetryCommonProperties(() => "ADirectory", userLevelCacheWriter: new NothingCache()); + unitUnderTest.GetTelemetryCommonProperties()["Current Path Hash"].Should().NotBe("ADirectory"); + } + + [Fact] + public void TelemetryCommonPropertiesShouldReturnHashedMachineId() + { + var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => "plaintext", userLevelCacheWriter: new NothingCache()); + unitUnderTest.GetTelemetryCommonProperties()["Machine ID"].Should().NotBe("plaintext"); + } + + [Fact] + public void TelemetryCommonPropertiesShouldReturnNewGuidWhenCannotGetMacAddress() + { + var unitUnderTest = new TelemetryCommonProperties(getMACAddress: () => null, userLevelCacheWriter: new NothingCache()); + var assignedMachineId = unitUnderTest.GetTelemetryCommonProperties()["Machine ID"]; + + Guid.TryParse(assignedMachineId, out var _).Should().BeTrue("it should be a guid"); + } + + private class NothingCache : IUserLevelCacheWriter + { + public string RunWithCache(string cacheKey, Func getValueToCache) + { + return getValueToCache(); + } + } + } +} diff --git a/test/msbuild.IntegrationTests/FakeRecordEventNameTelemetry.cs b/test/msbuild.IntegrationTests/FakeRecordEventNameTelemetry.cs new file mode 100644 index 000000000..091ededb3 --- /dev/null +++ b/test/msbuild.IntegrationTests/FakeRecordEventNameTelemetry.cs @@ -0,0 +1,38 @@ +// 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.Collections.Concurrent; +using System.Collections.Generic; +using Microsoft.DotNet.Cli; + +namespace Microsoft.DotNet.Cli.MSBuild.IntegrationTests +{ + public class FakeRecordEventNameTelemetry + { + public bool Enabled { get; set; } + + public string EventName { get; set; } + + public void TrackEvent(string eventName, + IDictionary properties, + IDictionary measurements) + { + LogEntries.Add( + new LogEntry + { + EventName = eventName, + Measurement = measurements, + Properties = properties + }); + } + + public ConcurrentBag LogEntries { get; set; } = new ConcurrentBag(); + + public class LogEntry + { + public string EventName { get; set; } + public IDictionary Properties { get; set; } + public IDictionary Measurement { get; set; } + } + } +} From 68d211795a08019ef33c2ab555baef2dba7135b4 Mon Sep 17 00:00:00 2001 From: William Li Date: Thu, 27 Jul 2017 10:41:36 -0700 Subject: [PATCH 15/20] Update SDK to 2.0.0-preview3-20170727-1 --- build/DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index e006cf7f3..f7bf8414e 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -12,7 +12,7 @@ - 2.0.0-preview3-20170726-3 + 2.0.0-preview3-20170727-1 $(CLI_NETSDK_Version) 4.3.0-rtm-4324 From 20a7ab075a19925fae6275ea928f1875c2b93509 Mon Sep 17 00:00:00 2001 From: William Li Date: Thu, 27 Jul 2017 11:32:59 -0700 Subject: [PATCH 16/20] Add the missing / --- build/package/Installer.RPM.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/package/Installer.RPM.targets b/build/package/Installer.RPM.targets index f152fb43a..f80ba792c 100644 --- a/build/package/Installer.RPM.targets +++ b/build/package/Installer.RPM.targets @@ -56,7 +56,7 @@ $(AspNetCoreVersion)-$(AspNetCoreRelease)-$(AspNetCoreRuntimePackageTimestamp) $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeRpmInstallerFileName).rpm $(ScriptsDir)/$(AfterInstallHostScriptName) - $(RpmLayoutScripts)$(AfterInstallHostScriptName) + $(RpmLayoutScripts)/$(AfterInstallHostScriptName) From 09cfa32e5b7db2a57260e36a699e4ce48e2e1034 Mon Sep 17 00:00:00 2001 From: Mike Lorbetske Date: Thu, 27 Jul 2017 12:44:00 -0700 Subject: [PATCH 17/20] Absorb update to MSTest version --- build/DependencyVersions.props | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index 0c861fd72..cb2674803 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -23,8 +23,8 @@ $(CLI_SharedFrameworkVersion) $(CLI_SharedFrameworkVersion) 1.0.0-beta2-20170719-291 - 1.0.0-beta2-20170725-300 - 1.0.0-beta2-20170725-300 + 1.0.0-beta2-20170727-301 + 1.0.0-beta2-20170727-301 2.0.0 2.0.0 0.1.1-alpha-167 From d864d600905469f997801a0e76a0a1d1b0edcbc1 Mon Sep 17 00:00:00 2001 From: Livar Date: Thu, 27 Jul 2017 15:03:18 -0700 Subject: [PATCH 18/20] Update SDK to 2.0.0-preview3-20170727-2 --- build/DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index 0c861fd72..29d3e8d2b 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -12,7 +12,7 @@ - 2.0.0-preview3-20170727-1 + 2.0.0-preview3-20170727-2 $(CLI_NETSDK_Version) 4.3.0-rtm-4324 From 53f6fe597f305913bfa691fb5355b3b06326e300 Mon Sep 17 00:00:00 2001 From: Livar Date: Thu, 27 Jul 2017 18:39:08 -0700 Subject: [PATCH 19/20] Update SDK to 2.0.0-preview3-20170728-1 Contains the new error message for .NET Framework referencing NetStandard 1.5 and 1.6 without the 2.0 SDK. --- build/DependencyVersions.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/DependencyVersions.props b/build/DependencyVersions.props index a14825769..d5257c77b 100644 --- a/build/DependencyVersions.props +++ b/build/DependencyVersions.props @@ -12,7 +12,7 @@ - 2.0.0-preview3-20170727-2 + 2.0.0-preview3-20170728-1 $(CLI_NETSDK_Version) 4.3.0-rtm-4324 From be54917dc4adb455569dca17bd979e55fd55990e Mon Sep 17 00:00:00 2001 From: William Lee Date: Fri, 28 Jul 2017 20:34:57 -0700 Subject: [PATCH 20/20] ingest store without timestamp (#7317) - Handling no timestamp aspnetcore-store for deb native installers - Handling no timestamp aspnetcore-store for rpm native installers AspNetCoreRuntimePackageName and the version in RPM package. - Align the name for debian 8 and rhel 7 package. --- build/package/Installer.DEB.targets | 3 ++- build/package/Installer.RPM.targets | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/build/package/Installer.DEB.targets b/build/package/Installer.DEB.targets index f068f9bcf..b041bc344 100644 --- a/build/package/Installer.DEB.targets +++ b/build/package/Installer.DEB.targets @@ -36,9 +36,10 @@ $(HostFxrDebianPackageName.ToLower()) dotnet-host $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp) + $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion) $(HostRid) - debian-x64 $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeDebInstallerFileName).deb + $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)-$(HostRidInAspNetCoreRuntimeDebInstallerFileName).deb diff --git a/build/package/Installer.RPM.targets b/build/package/Installer.RPM.targets index f80ba792c..1781d689b 100644 --- a/build/package/Installer.RPM.targets +++ b/build/package/Installer.RPM.targets @@ -51,10 +51,12 @@ $(HostFxrRpmPackageName.ToLower()) dotnet-host $(HostRid) - rhel-x64 $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp) + $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion) $(AspNetCoreVersion)-$(AspNetCoreRelease)-$(AspNetCoreRuntimePackageTimestamp) + $(AspNetCoreVersion) $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeRpmInstallerFileName).rpm + $(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)-$(HostRidInAspNetCoreRuntimeRpmInstallerFileName).rpm $(ScriptsDir)/$(AfterInstallHostScriptName) $(RpmLayoutScripts)/$(AfterInstallHostScriptName) @@ -243,4 +245,4 @@ - \ No newline at end of file +