Merge pull request #7321 from dotnet/merges/release/2.0.0-to-master-20170731-070024
Merge release/2.0.0 to master
This commit is contained in:
commit
51567d7641
73 changed files with 1717 additions and 157 deletions
|
@ -4,15 +4,15 @@
|
|||
<CLI_SharedFrameworkVersion>2.0.0</CLI_SharedFrameworkVersion>
|
||||
<CLI_MSBuild_Version>15.3.409</CLI_MSBuild_Version>
|
||||
<CLI_Roslyn_Version>2.3.2-beta1-61921-05</CLI_Roslyn_Version>
|
||||
<CLI_Roslyn_Satellites_Version>2.3.0-pre-20170624-6</CLI_Roslyn_Satellites_Version>
|
||||
<CLI_Roslyn_Satellites_Version>2.3.0-pre-20170727-1</CLI_Roslyn_Satellites_Version>
|
||||
<CLI_DiaSymNative_Version>1.6.0-beta2-25304</CLI_DiaSymNative_Version>
|
||||
<CLI_FSharp_Version>4.2.0-rc-170630-0</CLI_FSharp_Version>
|
||||
<CLI_FSharp_Satellites_Version>4.4.1-pre-20170624-6</CLI_FSharp_Satellites_Version>
|
||||
<CLI_FSharp_Satellites_Version>4.4.1-pre-20170727-1</CLI_FSharp_Satellites_Version>
|
||||
|
||||
<!-- We'll usually want to keep these versions in sync, but we may want to diverge in some
|
||||
cases, so use separate properties but derive one from the other unless we want to
|
||||
explicitly use different versions. -->
|
||||
<CLI_NETSDK_Version>2.0.0-preview3-20170721-7</CLI_NETSDK_Version>
|
||||
<CLI_NETSDK_Version>2.0.0-preview3-20170728-1</CLI_NETSDK_Version>
|
||||
<CLI_MSBuildExtensions_Version>$(CLI_NETSDK_Version)</CLI_MSBuildExtensions_Version>
|
||||
|
||||
<CLI_NuGet_Version>4.3.0-rtm-4324</CLI_NuGet_Version>
|
||||
|
@ -23,14 +23,14 @@
|
|||
<SharedHostVersion>$(CLI_SharedFrameworkVersion)</SharedHostVersion>
|
||||
<HostFxrVersion>$(CLI_SharedFrameworkVersion)</HostFxrVersion>
|
||||
<TemplateEngineVersion>1.0.0-beta2-20170719-291</TemplateEngineVersion>
|
||||
<TemplateEngineTemplateVersion>1.0.0-beta2-20170725-300</TemplateEngineTemplateVersion>
|
||||
<TemplateEngineTemplate2_0Version>1.0.0-beta2-20170725-300</TemplateEngineTemplate2_0Version>
|
||||
<TemplateEngineTemplateVersion>1.0.0-beta2-20170727-301</TemplateEngineTemplateVersion>
|
||||
<TemplateEngineTemplate2_0Version>1.0.0-beta2-20170727-301</TemplateEngineTemplate2_0Version>
|
||||
<PlatformAbstractionsVersion>2.0.0</PlatformAbstractionsVersion>
|
||||
<DependencyModelVersion>2.0.0</DependencyModelVersion>
|
||||
<CliCommandLineParserVersion>0.1.1-alpha-167</CliCommandLineParserVersion>
|
||||
<CliMigrateVersion>1.2.1-alpha-002133</CliMigrateVersion>
|
||||
<MicroBuildVersion>0.2.0</MicroBuildVersion>
|
||||
<SpaTemplateVersion>1.0.0-preview-000409</SpaTemplateVersion>
|
||||
<SpaTemplateVersion>1.0.417</SpaTemplateVersion>
|
||||
<XliffTasksVersion>0.2.0-beta-000042</XliffTasksVersion>
|
||||
|
||||
<!-- This should either be timestamped or notimestamp as appropriate -->
|
||||
|
|
|
@ -43,13 +43,15 @@
|
|||
SkipUnchangedFiles="False"
|
||||
UseHardlinksIfPossible="False" />
|
||||
|
||||
<!-- Create layout: postinst -->
|
||||
<Copy
|
||||
DestinationFiles= "$(DebianPostinstFile)"
|
||||
SourceFiles="$(DebianPostinstTemplateFile)"
|
||||
OverwriteReadOnlyFiles="True"
|
||||
SkipUnchangedFiles="False"
|
||||
UseHardlinksIfPossible="False" />
|
||||
<!-- Create layout: Generate and Place postinst -->
|
||||
<ReplaceFileContents
|
||||
InputFile="$(DebianPostinstTemplateFile)"
|
||||
DestinationFile="$(DebianPostinstFile)"
|
||||
ReplacementItems="@(DebianPostInstTokenValues)" />
|
||||
|
||||
<Chmod
|
||||
Glob="$(DebianPostinstFile)"
|
||||
Mode="ugo+x" />
|
||||
|
||||
<!-- Create layout: Generate and Place debian_config.json -->
|
||||
<ReplaceFileContents
|
||||
|
|
|
@ -36,9 +36,10 @@
|
|||
<HostFxrDebianPackageName>$(HostFxrDebianPackageName.ToLower())</HostFxrDebianPackageName>
|
||||
<HostDebianPackageName>dotnet-host</HostDebianPackageName>
|
||||
<AspNetCoreRuntimePackageName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageName>
|
||||
<AspNetCoreRuntimePackageName Condition=" '$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp' ">$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)</AspNetCoreRuntimePackageName>
|
||||
<HostRidInAspNetCoreRuntimeDebInstallerFileName>$(HostRid)</HostRidInAspNetCoreRuntimeDebInstallerFileName>
|
||||
<HostRidInAspNetCoreRuntimeDebInstallerFileName Condition=" '$(HostRid)' == 'debian.8-x64' ">debian-x64</HostRidInAspNetCoreRuntimeDebInstallerFileName>
|
||||
<AspNetCoreRuntimeDebInstallerFileName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeDebInstallerFileName).deb</AspNetCoreRuntimeDebInstallerFileName>
|
||||
<AspNetCoreRuntimeDebInstallerFileName Condition=" '$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp' ">$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)-$(HostRidInAspNetCoreRuntimeDebInstallerFileName).deb</AspNetCoreRuntimeDebInstallerFileName>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Inputs -->
|
||||
|
@ -105,6 +106,10 @@
|
|||
<DebianConfigTokenValues Include="%CLI_SDK_BRAND_NAME%">
|
||||
<ReplacementString>$(SdkBrandName)</ReplacementString>
|
||||
</DebianConfigTokenValues>
|
||||
|
||||
<DebianPostInstTokenValues Include="%SDK_VERSION%">
|
||||
<ReplacementString>$(SdkVersion)</ReplacementString>
|
||||
</DebianPostInstTokenValues>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -14,6 +14,9 @@
|
|||
|
||||
<SdkPkgSourcesRootDirectory>$(RepoRoot)/packaging/osx/clisdk</SdkPkgSourcesRootDirectory>
|
||||
<SdkPkgScriptsDirectory>$(SdkPkgSourcesRootDirectory)/scripts</SdkPkgScriptsDirectory>
|
||||
<SdkPkgScriptTemplateFile>$(SdkPkgScriptsDirectory)/postinstall</SdkPkgScriptTemplateFile>
|
||||
<SdkPkgDestinationScriptsDirectory>$(PkgIntermediateDirectory)/scripts</SdkPkgDestinationScriptsDirectory>
|
||||
<SdkPkgScriptFile>$(SdkPkgDestinationScriptsDirectory)/postinstall</SdkPkgScriptFile>
|
||||
<SdkProductArchiveResourcesDirectory>$(SdkPkgSourcesRootDirectory)/resources</SdkProductArchiveResourcesDirectory>
|
||||
|
||||
<SdkProductArchiveDistributionTemplateFile>$(SdkPkgSourcesRootDirectory)/Distribution-Template</SdkProductArchiveDistributionTemplateFile>
|
||||
|
@ -54,6 +57,10 @@
|
|||
<DistributionTemplateReplacement Include="{HostFxrBrandName}">
|
||||
<ReplacementString>$(HostFxrBrandName)</ReplacementString>
|
||||
</DistributionTemplateReplacement>
|
||||
|
||||
<PostInstallScriptReplacement Include="%SDK_VERSION%">
|
||||
<ReplacementString>$(SdkVersion)</ReplacementString>
|
||||
</PostInstallScriptReplacement>
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Consumed By Publish -->
|
||||
|
@ -83,12 +90,22 @@
|
|||
<Copy SourceFiles="@(AspNetRuntimeFilesInput)"
|
||||
DestinationFiles="@(AspNetRuntimeFilesInput->'$(SdkLayoutOutputDirectory)/%(RecursiveDir)%(FileName)%(Extension)')" />
|
||||
|
||||
<ReplaceFileContents
|
||||
InputFile="$(SdkPkgScriptTemplateFile)"
|
||||
DestinationFile="$(SdkPkgScriptFile)"
|
||||
ReplacementPatterns="@(PostInstallScriptReplacement -> '%(Identity)')"
|
||||
ReplacementStrings="@(PostInstallScriptReplacement -> '%(ReplacementString)')" />
|
||||
|
||||
<Chmod
|
||||
Glob="$(SdkPkgScriptFile)"
|
||||
Mode="ugo+x" />
|
||||
|
||||
<Exec Command="pkgbuild
|
||||
--root '$(SdkLayoutOutputDirectory)'
|
||||
--identifier '$(SdkComponentId)'
|
||||
--version '$(SdkVersion)'
|
||||
--install-location '$(PkgInstallDirectory)'
|
||||
--scripts '$(SdkPkgScriptsDirectory)'
|
||||
--scripts '$(SdkPkgDestinationScriptsDirectory)'
|
||||
'$(SdkInstallerFile)'" />
|
||||
</Target>
|
||||
|
||||
|
|
|
@ -4,5 +4,6 @@
|
|||
<RpmConfigJsonName>rpm_config.json</RpmConfigJsonName>
|
||||
<TemplatesDir>$(RepoRoot)/packaging/rpm/templates</TemplatesDir>
|
||||
<ScriptsDir>$(RepoRoot)/packaging/rpm/scripts</ScriptsDir>
|
||||
<AfterInstallHostScriptName>after_install_host.sh</AfterInstallHostScriptName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
|
@ -51,10 +51,14 @@
|
|||
<HostFxrRpmPackageName>$(HostFxrRpmPackageName.ToLower())</HostFxrRpmPackageName>
|
||||
<HostRpmPackageName>dotnet-host</HostRpmPackageName>
|
||||
<HostRidInAspNetCoreRuntimeRpmInstallerFileName>$(HostRid)</HostRidInAspNetCoreRuntimeRpmInstallerFileName>
|
||||
<HostRidInAspNetCoreRuntimeRpmInstallerFileName Condition=" '$(HostRid)' == 'rhel.7-x64' ">rhel-x64</HostRidInAspNetCoreRuntimeRpmInstallerFileName>
|
||||
<AspNetCoreRuntimePackageName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageName>
|
||||
<AspNetCoreRuntimePackageName Condition="'$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp'">$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)</AspNetCoreRuntimePackageName>
|
||||
<AspNetCoreRuntimePackageVersion>$(AspNetCoreVersion)-$(AspNetCoreRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageVersion>
|
||||
<AspNetCoreRuntimePackageVersion Condition="'$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp'">$(AspNetCoreVersion)</AspNetCoreRuntimePackageVersion>
|
||||
<AspNetCoreRuntimeRpmInstallerFileName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeRpmInstallerFileName).rpm</AspNetCoreRuntimeRpmInstallerFileName>
|
||||
<AspNetCoreRuntimeRpmInstallerFileName Condition="'$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp'">$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)-$(HostRidInAspNetCoreRuntimeRpmInstallerFileName).rpm</AspNetCoreRuntimeRpmInstallerFileName>
|
||||
<AfterInstallHostScriptTemplateFile>$(ScriptsDir)/$(AfterInstallHostScriptName)</AfterInstallHostScriptTemplateFile>
|
||||
<AfterInstallHostScriptDestinationFile>$(RpmLayoutScripts)/$(AfterInstallHostScriptName)</AfterInstallHostScriptDestinationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
@ -78,7 +82,7 @@
|
|||
<SDKFiles Include="$(InputRoot)/**/*"/>
|
||||
<SDKManpages Include="$(ManPagesDir)/**/*"/>
|
||||
<SDKTemplatesFiles Include="$(TemplatesDir)/**/*"/>
|
||||
<SDKScriptsFiles Include="$(ScriptsDir)/**/*"/>
|
||||
<SDKScriptsFiles Include="$(ScriptsDir)/after_remove_host.sh"/>
|
||||
</ItemGroup>
|
||||
|
||||
<Copy SourceFiles="@(SDKFiles)"
|
||||
|
@ -128,6 +132,10 @@
|
|||
<SDKTokenValue Include="%SDK_RPM_PACKAGE_NAME%">
|
||||
<ReplacementString>$(SdkRpmPackageName)</ReplacementString>
|
||||
</SDKTokenValue>
|
||||
|
||||
<AfterInstallHostTokenValue Include="%SDK_VERSION%">
|
||||
<ReplacementString>$(SdkVersion)</ReplacementString>
|
||||
</AfterInstallHostTokenValue>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -135,6 +143,14 @@
|
|||
<GeneratedInstallers Include="$(SdkInstallerFile)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ReplaceFileContents InputFile="$(AfterInstallHostScriptTemplateFile)"
|
||||
DestinationFile="$(AfterInstallHostScriptDestinationFile)"
|
||||
ReplacementItems="@(AfterInstallHostTokenValue)"/>
|
||||
|
||||
<Chmod
|
||||
Glob="$(AfterInstallHostScriptDestinationFile)"
|
||||
Mode="ugo+x" />
|
||||
|
||||
<ReplaceFileContents InputFile="$(ConfigJsonFile)"
|
||||
DestinationFile="$(RpmLayoutDirectory)$(RpmConfigJsonName)"
|
||||
ReplacementItems="@(SDKTokenValue)"/>
|
||||
|
@ -229,4 +245,4 @@
|
|||
<Exec Command="sudo yum remove -y $(HostFxrRpmPackageName)" />
|
||||
<Exec Command="sudo yum remove -y $(HostRpmPackageName)" />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -13,6 +13,13 @@
|
|||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica;padding-left:10px">
|
||||
<p>This product collects usage data</h2>
|
||||
<ul>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">More information and opt-out</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<br>
|
||||
<div align="left" style="font-family: Helvetica">
|
||||
<h2 style="padding-left:10px"> Resources </h2>
|
||||
<ul>
|
||||
|
@ -20,7 +27,6 @@
|
|||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
||||
<li><a href="https://aka.ms/20-p2-rel-notes">Release Notes</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-tutorials">Tutorials</a></li>
|
||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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</String>
|
||||
• Tutorials https://aka.ms/dotnet-tutorials</String>
|
||||
<String Id="WelcomeHeaderMessage">.NET Core SDK</String>
|
||||
<String Id="WelcomeDescription">
|
||||
.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!</String>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<Property Id="ProductCPU" Value="$(var.Platform)" />
|
||||
<Property Id="RTM_ProductVersion" Value="$(var.Dotnet_ProductVersion)" />
|
||||
<Property Id="MSIFASTINSTALL" Value="7" />
|
||||
<Property Id="NUGETVERSION" Value="$(var.NugetVersion)" />
|
||||
<WixVariable Id="WixUILicenseRtf" Value="$(var.MicrosoftEula)" />
|
||||
|
||||
<Property Id="DOTNETEXE">
|
||||
|
@ -32,7 +33,7 @@
|
|||
|
||||
<CustomAction Id="PropertyAssignPrimeCacheAndTelemetry"
|
||||
Property="QtExecPrimeCacheAndTelemetryTarget"
|
||||
Value=""[DOTNETHOME]\dotnet.exe" internal-reportinstallsuccess "[EXEFULLPATH]""
|
||||
Value=""[DOTNETHOME]\dotnet.exe" exec "[DOTNETHOME]\sdk\[NUGETVERSION]\dotnet.dll" internal-reportinstallsuccess "[EXEFULLPATH]""
|
||||
Execute="immediate" />
|
||||
<CustomAction Id="QtExecPrimeCacheAndTelemetryTarget"
|
||||
BinaryKey="WixCA"
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Rozbalení</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Dekomprese</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Aufklappen</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Dekomprimieren</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Expandiendo</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Descomprimiendo</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Développement</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Décompression</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Espansione</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Decompressione</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">展開中</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">圧縮解除中</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">확장하는 중</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">압축을 푸는 중</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Rozwijanie</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Dekompresja</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Expandindo</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Descompactando</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Идет расширение</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Идет извлечение</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">Genişletme</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">Daraltma</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">正在扩展</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">正在解压缩</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
<body>
|
||||
<trans-unit id="Expanding">
|
||||
<source>Expanding</source>
|
||||
<target state="new">Expanding</target>
|
||||
<target state="translated">正在展開</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
<trans-unit id="Decompressing">
|
||||
<source>Decompressing</source>
|
||||
<target state="new">Decompressing</target>
|
||||
<target state="translated">正在解壓縮</target>
|
||||
<note />
|
||||
</trans-unit>
|
||||
</body>
|
||||
|
|
12
src/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs
Normal file
12
src/Microsoft.DotNet.Cli.Utils/ITelemetryFilter.cs
Normal file
|
@ -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<ApplicationInsightsEntryFormat> Filter(object o);
|
||||
}
|
||||
}
|
87
src/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs
Normal file
87
src/Microsoft.DotNet.Cli.Utils/TelemetryEventEntry.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Utils
|
||||
{
|
||||
public static class TelemetryEventEntry
|
||||
{
|
||||
public static event EventHandler<InstrumentationEventArgs> EntryPosted;
|
||||
public static ITelemetryFilter TelemetryFilter { get; set; } = new BlockFilter();
|
||||
|
||||
public static void TrackEvent(
|
||||
string eventName = null,
|
||||
IDictionary<string, string> properties = null,
|
||||
IDictionary<string, double> 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<string,
|
||||
IDictionary<string, string>,
|
||||
IDictionary<string, double>> subscriber)
|
||||
{
|
||||
void Handler(object sender, InstrumentationEventArgs eventArgs)
|
||||
{
|
||||
subscriber(eventArgs.EventName, eventArgs.Properties, eventArgs.Measurements);
|
||||
}
|
||||
|
||||
EntryPosted += Handler;
|
||||
}
|
||||
}
|
||||
|
||||
public class BlockFilter : ITelemetryFilter
|
||||
{
|
||||
public IEnumerable<ApplicationInsightsEntryFormat> Filter(object o)
|
||||
{
|
||||
return new List<ApplicationInsightsEntryFormat>();
|
||||
}
|
||||
}
|
||||
|
||||
public class InstrumentationEventArgs : EventArgs
|
||||
{
|
||||
internal InstrumentationEventArgs(
|
||||
string eventName,
|
||||
IDictionary<string, string> properties,
|
||||
IDictionary<string, double> measurements)
|
||||
{
|
||||
EventName = eventName;
|
||||
Properties = properties;
|
||||
Measurements = measurements;
|
||||
}
|
||||
|
||||
public string EventName { get; }
|
||||
public IDictionary<string, string> Properties { get; }
|
||||
public IDictionary<string, double> Measurements { get; }
|
||||
}
|
||||
|
||||
public class ApplicationInsightsEntryFormat
|
||||
{
|
||||
public ApplicationInsightsEntryFormat(
|
||||
string eventName = null,
|
||||
IDictionary<string, string> properties = null,
|
||||
IDictionary<string, double> measurements = null)
|
||||
{
|
||||
EventName = eventName;
|
||||
Properties = properties;
|
||||
Measurements = measurements;
|
||||
}
|
||||
|
||||
public string EventName { get; }
|
||||
public IDictionary<string, string> Properties { get; }
|
||||
public IDictionary<string, double> Measurements { get; }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
12
src/Microsoft.DotNet.Configurer/IUserLevelCacheWriter.cs
Normal file
12
src/Microsoft.DotNet.Configurer/IUserLevelCacheWriter.cs
Normal file
|
@ -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<string> getValueToCache);
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ namespace Microsoft.DotNet.Configurer
|
|||
|
||||
public bool InProgressSentinelAlreadyExists()
|
||||
{
|
||||
return CouldNotGetAHandleToTheInProgressSentinel();
|
||||
return CouldNotGetAHandleToTheInProgressSentinel() && !UnauthorizedAccess;
|
||||
}
|
||||
|
||||
public bool Exists()
|
||||
|
|
73
src/Microsoft.DotNet.Configurer/UserLevelCacheWriter.cs
Normal file
73
src/Microsoft.DotNet.Configurer/UserLevelCacheWriter.cs
Normal file
|
@ -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<string> 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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -80,8 +82,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"))
|
||||
|
@ -100,8 +104,8 @@ namespace Microsoft.DotNet.Cli
|
|||
return 0;
|
||||
}
|
||||
else if (IsArg(args[lastArg], "h", "help") ||
|
||||
args[lastArg] == "-?" ||
|
||||
args[lastArg] == "/?")
|
||||
args[lastArg] == "-?" ||
|
||||
args[lastArg] == "/?")
|
||||
{
|
||||
HelpCommand.PrintHelp();
|
||||
return 0;
|
||||
|
@ -113,10 +117,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;
|
||||
}
|
||||
}
|
||||
|
@ -128,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<string>() : args.Skip(lastArg + 1).ToArray();
|
||||
IEnumerable<string> appArgs =
|
||||
(lastArg + 1) >= args.Length
|
||||
? Enumerable.Empty<string>()
|
||||
: args.Skip(lastArg + 1).ToArray();
|
||||
|
||||
if (CommandContext.IsVerbose())
|
||||
{
|
||||
|
@ -144,12 +162,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
|
||||
|
@ -161,9 +179,12 @@ namespace Microsoft.DotNet.Cli
|
|||
.Execute();
|
||||
exitCode = result.ExitCode;
|
||||
}
|
||||
|
||||
return exitCode;
|
||||
}
|
||||
|
||||
private static bool IsDotnetBeingInvokedFromNativeInstaller(string command)
|
||||
{
|
||||
return command == "internal-reportinstallsuccess";
|
||||
}
|
||||
|
||||
private static void ConfigureDotNetForFirstTimeUse(
|
||||
|
|
42
src/dotnet/Telemetry/AllowListToSendFirstAppliedOptions.cs
Normal file
42
src/dotnet/Telemetry/AllowListToSendFirstAppliedOptions.cs
Normal file
|
@ -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<string> topLevelCommandNameAllowList)
|
||||
{
|
||||
_topLevelCommandNameAllowList = topLevelCommandNameAllowList;
|
||||
}
|
||||
|
||||
private HashSet<string> _topLevelCommandNameAllowList { get; }
|
||||
|
||||
public List<ApplicationInsightsEntryFormat> AllowList(ParseResult parseResult)
|
||||
{
|
||||
var topLevelCommandNameFromParse = parseResult["dotnet"]?.AppliedOptions?.FirstOrDefault()?.Name;
|
||||
var result = new List<ApplicationInsightsEntryFormat>();
|
||||
if (_topLevelCommandNameAllowList.Contains(topLevelCommandNameFromParse))
|
||||
{
|
||||
var firstOption = parseResult["dotnet"]?[topLevelCommandNameFromParse]
|
||||
?.AppliedOptions?.FirstOrDefault()?.Name;
|
||||
if (firstOption != null)
|
||||
{
|
||||
result.Add(new ApplicationInsightsEntryFormat(
|
||||
"dotnet-" + topLevelCommandNameFromParse,
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
{"argument", firstOption}
|
||||
}));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
45
src/dotnet/Telemetry/AllowListToSendFirstArgument.cs
Normal file
45
src/dotnet/Telemetry/AllowListToSendFirstArgument.cs
Normal file
|
@ -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<string> topLevelCommandNameAllowList)
|
||||
{
|
||||
_topLevelCommandNameAllowList = topLevelCommandNameAllowList;
|
||||
}
|
||||
|
||||
private HashSet<string> _topLevelCommandNameAllowList { get; }
|
||||
|
||||
public List<ApplicationInsightsEntryFormat> AllowList(ParseResult parseResult)
|
||||
{
|
||||
var result = new List<ApplicationInsightsEntryFormat>();
|
||||
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<string, string>
|
||||
{
|
||||
{"argument", firstArgument}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
58
src/dotnet/Telemetry/DockerContainerDetectorForTelemetry.cs
Normal file
58
src/dotnet/Telemetry/DockerContainerDetectorForTelemetry.cs
Normal file
|
@ -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
|
||||
}
|
||||
}
|
10
src/dotnet/Telemetry/IDockerContainerDetector.cs
Normal file
10
src/dotnet/Telemetry/IDockerContainerDetector.cs
Normal file
|
@ -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();
|
||||
}
|
||||
}
|
14
src/dotnet/Telemetry/IParseResultLogRule.cs
Normal file
14
src/dotnet/Telemetry/IParseResultLogRule.cs
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.DotNet.Cli.CommandLine;
|
||||
using Microsoft.DotNet.Cli.Utils;
|
||||
|
||||
namespace Microsoft.DotNet.Cli.Telemetry
|
||||
{
|
||||
internal interface IParseResultLogRule
|
||||
{
|
||||
List<ApplicationInsightsEntryFormat> AllowList(ParseResult parseResult);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
168
src/dotnet/Telemetry/MacAddressGetter.cs
Normal file
168
src/dotnet/Telemetry/MacAddressGetter.cs
Normal file
|
@ -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<string> GetMacAddressesByNetworkInterface()
|
||||
{
|
||||
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
|
||||
var macs = new List<string>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
32
src/dotnet/Telemetry/Sha256Hasher.cs
Normal file
32
src/dotnet/Telemetry/Sha256Hasher.cs
Normal file
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// // 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)
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,40 +4,32 @@
|
|||
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<string, string> _commonProperties = null;
|
||||
private Dictionary<string, double> _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) { }
|
||||
|
||||
public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId)
|
||||
public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId, bool blockThreadInitialization = false)
|
||||
{
|
||||
Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel);
|
||||
|
||||
|
@ -49,8 +41,15 @@ namespace Microsoft.DotNet.Cli
|
|||
// 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());
|
||||
if (blockThreadInitialization)
|
||||
{
|
||||
InitializeTelemetry();
|
||||
}
|
||||
else
|
||||
{
|
||||
//initialize in task to offload to parallel thread
|
||||
_trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry());
|
||||
}
|
||||
}
|
||||
|
||||
private bool PermissionExists(IFirstTimeUseNoticeSentinel sentinel)
|
||||
|
@ -63,7 +62,8 @@ namespace Microsoft.DotNet.Cli
|
|||
return sentinel.Exists();
|
||||
}
|
||||
|
||||
public void TrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> measurements)
|
||||
public void TrackEvent(string eventName, IDictionary<string, string> properties,
|
||||
IDictionary<string, double> measurements)
|
||||
{
|
||||
if (!Enabled)
|
||||
{
|
||||
|
@ -92,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<string, string>();
|
||||
_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<string, double>();
|
||||
}
|
||||
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<string, string> properties, IDictionary<string, double> measurements)
|
||||
private void TrackEventTask(
|
||||
string eventName,
|
||||
IDictionary<string, string> properties,
|
||||
IDictionary<string, double> measurements)
|
||||
{
|
||||
if (_client == null)
|
||||
{
|
||||
|
@ -120,15 +117,15 @@ namespace Microsoft.DotNet.Cli
|
|||
|
||||
try
|
||||
{
|
||||
var eventProperties = GetEventProperties(properties);
|
||||
var eventMeasurements = GetEventMeasures(measurements);
|
||||
Dictionary<string, string> eventProperties = GetEventProperties(properties);
|
||||
Dictionary<string, double> eventMeasurements = GetEventMeasures(measurements);
|
||||
|
||||
_client.TrackEvent(eventName, eventProperties, eventMeasurements);
|
||||
_client.Flush();
|
||||
}
|
||||
catch (Exception)
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.Fail("Exception during TrackEventTask");
|
||||
Debug.Fail(e.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,7 +134,7 @@ namespace Microsoft.DotNet.Cli
|
|||
Dictionary<string, double> eventMeasurements = new Dictionary<string, double>(_commonMeasurements);
|
||||
if (measurements != null)
|
||||
{
|
||||
foreach (var measurement in measurements)
|
||||
foreach (KeyValuePair<string, double> measurement in measurements)
|
||||
{
|
||||
if (eventMeasurements.ContainsKey(measurement.Key))
|
||||
{
|
||||
|
@ -157,7 +154,7 @@ namespace Microsoft.DotNet.Cli
|
|||
if (properties != null)
|
||||
{
|
||||
var eventProperties = new Dictionary<string, string>(_commonProperties);
|
||||
foreach (var property in properties)
|
||||
foreach (KeyValuePair<string, string> property in properties)
|
||||
{
|
||||
if (eventProperties.ContainsKey(property.Key))
|
||||
{
|
77
src/dotnet/Telemetry/TelemetryCommonProperties.cs
Normal file
77
src/dotnet/Telemetry/TelemetryCommonProperties.cs
Normal file
|
@ -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<string> getCurrentDirectory = null,
|
||||
Func<string, string> hasher = null,
|
||||
Func<string> 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<string> _getCurrentDirectory;
|
||||
private Func<string, string> _hasher;
|
||||
private Func<string> _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<string, string> GetTelemetryCommonProperties()
|
||||
{
|
||||
return new Dictionary<string, string>
|
||||
{
|
||||
{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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
95
src/dotnet/Telemetry/TelemetryFilter.cs
Normal file
95
src/dotnet/Telemetry/TelemetryFilter.cs
Normal file
|
@ -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<ApplicationInsightsEntryFormat> Filter(object objectToFilter)
|
||||
{
|
||||
var ruleSet = new List<IParseResultLogRule>
|
||||
{ new AllowListToSendFirstArgument(new HashSet<string>{ "new", "help" }),
|
||||
new AllowListToSendFirstAppliedOptions(new HashSet<string>{ "add", "remove", "list", "sln", "nuget" }),
|
||||
new TopLevelCommandNameAndOptionToLog
|
||||
(
|
||||
topLevelCommandName: new HashSet<string> { "new" },
|
||||
optionsToLog: new HashSet<string> { "language" }
|
||||
),
|
||||
new TopLevelCommandNameAndOptionToLog
|
||||
(
|
||||
topLevelCommandName: new HashSet<string> { "build", "publish" },
|
||||
optionsToLog: new HashSet<string> { "framework", "runtime", "configuration" }
|
||||
),
|
||||
new TopLevelCommandNameAndOptionToLog
|
||||
(
|
||||
topLevelCommandName: new HashSet<string> { "run", "clean", "test" },
|
||||
optionsToLog: new HashSet<string> { "framework", "configuration" }
|
||||
),
|
||||
new TopLevelCommandNameAndOptionToLog
|
||||
(
|
||||
topLevelCommandName: new HashSet<string> { "pack" },
|
||||
optionsToLog: new HashSet<string> { "configuration" }
|
||||
),
|
||||
new TopLevelCommandNameAndOptionToLog
|
||||
(
|
||||
topLevelCommandName: new HashSet<string> { "migrate" },
|
||||
optionsToLog: new HashSet<string> { "sdk-package-version" }
|
||||
),
|
||||
new TopLevelCommandNameAndOptionToLog
|
||||
(
|
||||
topLevelCommandName: new HashSet<string> { "vstest" },
|
||||
optionsToLog: new HashSet<string> { "platform", "framework", "logger" }
|
||||
),
|
||||
new TopLevelCommandNameAndOptionToLog
|
||||
(
|
||||
topLevelCommandName: new HashSet<string> { "publish" },
|
||||
optionsToLog: new HashSet<string> { "runtime" }
|
||||
)
|
||||
};
|
||||
var result = new List<ApplicationInsightsEntryFormat>();
|
||||
|
||||
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<ApplicationInsightsEntryFormat> 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<string, string>()
|
||||
{
|
||||
{"verbosity", appliedOptions.Arguments.ElementAt(0)}
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
49
src/dotnet/Telemetry/TopLevelCommandNameAndOptionToLog.cs
Normal file
49
src/dotnet/Telemetry/TopLevelCommandNameAndOptionToLog.cs
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System.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<string> topLevelCommandName,
|
||||
HashSet<string> optionsToLog)
|
||||
{
|
||||
_topLevelCommandName = topLevelCommandName;
|
||||
_optionsToLog = optionsToLog;
|
||||
}
|
||||
|
||||
private HashSet<string> _topLevelCommandName { get; }
|
||||
private HashSet<string> _optionsToLog { get; }
|
||||
private const string DotnetName = "dotnet";
|
||||
|
||||
public List<ApplicationInsightsEntryFormat> AllowList(ParseResult parseResult)
|
||||
{
|
||||
var topLevelCommandName = parseResult[DotnetName]?.AppliedOptions?.FirstOrDefault()?.Name;
|
||||
var result = new List<ApplicationInsightsEntryFormat>();
|
||||
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<string, string>
|
||||
{
|
||||
{option, appliedOptions.Arguments.ElementAt(0)}
|
||||
}));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 FirstTimeUseNoticeSentinel(new CliFallbackFolderPathCalculator()), sessionId);
|
||||
telemetry = new Telemetry.Telemetry(new NoOpFirstTimeUseNoticeSentinel(), sessionId, blockThreadInitialization: true);
|
||||
}
|
||||
public bool Enabled => telemetry.Enabled;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -233,5 +233,73 @@ namespace Microsoft.DotNet.Configurer.UnitTests
|
|||
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once);
|
||||
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_prints_the_first_time_use_notice_if_the_cache_sentinel_exists_but_the_first_notice_sentinel_does_not()
|
||||
{
|
||||
_nugetCacheSentinelMock.Setup(n => n.Exists()).Returns(true);
|
||||
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
|
||||
|
||||
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
|
||||
_nugetCachePrimerMock.Object,
|
||||
_nugetCacheSentinelMock.Object,
|
||||
_firstTimeUseNoticeSentinelMock.Object,
|
||||
_environmentProviderMock.Object,
|
||||
_reporterMock.Object,
|
||||
CliFallbackFolderPath);
|
||||
|
||||
dotnetFirstTimeUseConfigurer.Configure();
|
||||
|
||||
_reporterMock.Verify(r =>
|
||||
r.WriteLine(It.Is<string>(str => str == LocalizableStrings.FirstTimeWelcomeMessage)));
|
||||
_reporterMock.Verify(
|
||||
r => r.WriteLine(It.Is<string>(str => str == LocalizableStrings.NugetCachePrimeMessage)),
|
||||
Times.Never);
|
||||
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_prints_the_unauthorized_notice_if_the_cache_sentinel_reports_Unauthorized()
|
||||
{
|
||||
_nugetCacheSentinelMock.Setup(n => n.UnauthorizedAccess).Returns(true);
|
||||
|
||||
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
|
||||
_nugetCachePrimerMock.Object,
|
||||
_nugetCacheSentinelMock.Object,
|
||||
_firstTimeUseNoticeSentinelMock.Object,
|
||||
_environmentProviderMock.Object,
|
||||
_reporterMock.Object,
|
||||
CliFallbackFolderPath);
|
||||
|
||||
dotnetFirstTimeUseConfigurer.Configure();
|
||||
|
||||
_reporterMock.Verify(r =>
|
||||
r.WriteLine(It.Is<string>(str => str == LocalizableStrings.FirstTimeWelcomeMessage)));
|
||||
_reporterMock.Verify(r =>
|
||||
r.WriteLine(It.Is<string>(str =>
|
||||
str == string.Format(LocalizableStrings.UnauthorizedAccessMessage, CliFallbackFolderPath))));
|
||||
_reporterMock.Verify(
|
||||
r => r.WriteLine(It.Is<string>(str => str == LocalizableStrings.NugetCachePrimeMessage)),
|
||||
Times.Never);
|
||||
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void It_does_not_prime_the_cache_if_the_cache_sentinel_reports_Unauthorized()
|
||||
{
|
||||
_nugetCacheSentinelMock.Setup(n => n.UnauthorizedAccess).Returns(true);
|
||||
|
||||
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
|
||||
_nugetCachePrimerMock.Object,
|
||||
_nugetCacheSentinelMock.Object,
|
||||
_firstTimeUseNoticeSentinelMock.Object,
|
||||
_environmentProviderMock.Object,
|
||||
_reporterMock.Object,
|
||||
CliFallbackFolderPath);
|
||||
|
||||
dotnetFirstTimeUseConfigurer.Configure();
|
||||
|
||||
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Never);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<string> 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<IFile>();
|
||||
|
||||
var systemUndertest =
|
||||
new UserLevelCacheWriter(
|
||||
DOTNET_USER_PROFILE_FOLDER_PATH,
|
||||
new NoPermissionFileFake(),
|
||||
new NoPermissionDirectoryFake());
|
||||
|
||||
var counter = new Counter();
|
||||
Func<string> 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<string> 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++; }
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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<bool> sentinelExists)
|
||||
{
|
||||
Telemetry telemetry;
|
||||
Telemetry.Telemetry telemetry;
|
||||
return GetArgsForMSBuild(sentinelExists, out telemetry);
|
||||
}
|
||||
|
||||
private string[] GetArgsForMSBuild(Func<bool> sentinelExists, out Telemetry telemetry)
|
||||
private string[] GetArgsForMSBuild(Func<bool> 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<string>());
|
||||
|
||||
|
|
|
@ -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}")
|
||||
|
|
39
test/dotnet.Tests/FakeRecordEventNameTelemetry.cs
Normal file
39
test/dotnet.Tests/FakeRecordEventNameTelemetry.cs
Normal file
|
@ -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<string, string> properties,
|
||||
IDictionary<string, double> measurements)
|
||||
{
|
||||
LogEntries.Add(
|
||||
new LogEntry
|
||||
{
|
||||
EventName = eventName,
|
||||
Measurement = measurements,
|
||||
Properties = properties
|
||||
});
|
||||
}
|
||||
|
||||
public ConcurrentBag<LogEntry> LogEntries { get; set; } = new ConcurrentBag<LogEntry>();
|
||||
|
||||
public class LogEntry
|
||||
{
|
||||
public string EventName { get; set; }
|
||||
public IDictionary<string, string> Properties { get; set; }
|
||||
public IDictionary<string, double> Measurement { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,61 @@ namespace Microsoft.DotNet.Tests
|
|||
.HaveFile($"{GetDotnetVersion()}.dotnetSentinel");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItCreatesAFirstUseSentinelFileUnderTheDotDotNetFolder()
|
||||
{
|
||||
_dotDotnetFolder
|
||||
.Should()
|
||||
.HaveFile($"{GetDotnetVersion()}.dotnetFirstUseSentinel");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ItDoesNotCreateAFirstUseSentinelFileUnderTheDotDotNetFolderWhenInternalReportInstallSuccessIsInvoked()
|
||||
{
|
||||
var emptyHome = Path.Combine(_testDirectory, "empty_home");
|
||||
|
||||
var command = new DotnetCommand()
|
||||
.WithWorkingDirectory(_testDirectory);
|
||||
command.Environment["HOME"] = emptyHome;
|
||||
command.Environment["USERPROFILE"] = emptyHome;
|
||||
command.Environment["APPDATA"] = emptyHome;
|
||||
command.Environment["DOTNET_CLI_TEST_FALLBACKFOLDER"] = _nugetFallbackFolder.FullName;
|
||||
command.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "";
|
||||
// Disable to prevent the creation of the .dotnet folder by optimizationdata.
|
||||
command.Environment["DOTNET_DISABLE_MULTICOREJIT"] = "true";
|
||||
command.Environment["SkipInvalidConfigurations"] = "true";
|
||||
|
||||
command.ExecuteWithCapturedOutput("internal-reportinstallsuccess test").Should().Pass();
|
||||
|
||||
var homeFolder = new DirectoryInfo(Path.Combine(emptyHome, ".dotnet"));
|
||||
string[] fileEntries = Directory.GetFiles(homeFolder.ToString());
|
||||
fileEntries.Should().OnlyContain(x => !x.Contains(".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()
|
||||
{
|
||||
|
|
|
@ -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<string, string> Properties { get; set; }
|
||||
|
||||
public void TrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> 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]
|
||||
|
|
54
test/dotnet.Tests/TelemetryCommonPropertiesTests.cs
Normal file
54
test/dotnet.Tests/TelemetryCommonPropertiesTests.cs
Normal file
|
@ -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<string> getValueToCache)
|
||||
{
|
||||
return getValueToCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<string, string> properties,
|
||||
IDictionary<string, double> measurements)
|
||||
{
|
||||
LogEntries.Add(
|
||||
new LogEntry
|
||||
{
|
||||
EventName = eventName,
|
||||
Measurement = measurements,
|
||||
Properties = properties
|
||||
});
|
||||
}
|
||||
|
||||
public ConcurrentBag<LogEntry> LogEntries { get; set; } = new ConcurrentBag<LogEntry>();
|
||||
|
||||
public class LogEntry
|
||||
{
|
||||
public string EventName { get; set; }
|
||||
public IDictionary<string, string> Properties { get; set; }
|
||||
public IDictionary<string, double> Measurement { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue