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_SharedFrameworkVersion>2.0.0</CLI_SharedFrameworkVersion>
|
||||||
<CLI_MSBuild_Version>15.3.409</CLI_MSBuild_Version>
|
<CLI_MSBuild_Version>15.3.409</CLI_MSBuild_Version>
|
||||||
<CLI_Roslyn_Version>2.3.2-beta1-61921-05</CLI_Roslyn_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_DiaSymNative_Version>1.6.0-beta2-25304</CLI_DiaSymNative_Version>
|
||||||
<CLI_FSharp_Version>4.2.0-rc-170630-0</CLI_FSharp_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
|
<!-- 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
|
cases, so use separate properties but derive one from the other unless we want to
|
||||||
explicitly use different versions. -->
|
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_MSBuildExtensions_Version>$(CLI_NETSDK_Version)</CLI_MSBuildExtensions_Version>
|
||||||
|
|
||||||
<CLI_NuGet_Version>4.3.0-rtm-4324</CLI_NuGet_Version>
|
<CLI_NuGet_Version>4.3.0-rtm-4324</CLI_NuGet_Version>
|
||||||
|
@ -23,14 +23,14 @@
|
||||||
<SharedHostVersion>$(CLI_SharedFrameworkVersion)</SharedHostVersion>
|
<SharedHostVersion>$(CLI_SharedFrameworkVersion)</SharedHostVersion>
|
||||||
<HostFxrVersion>$(CLI_SharedFrameworkVersion)</HostFxrVersion>
|
<HostFxrVersion>$(CLI_SharedFrameworkVersion)</HostFxrVersion>
|
||||||
<TemplateEngineVersion>1.0.0-beta2-20170719-291</TemplateEngineVersion>
|
<TemplateEngineVersion>1.0.0-beta2-20170719-291</TemplateEngineVersion>
|
||||||
<TemplateEngineTemplateVersion>1.0.0-beta2-20170725-300</TemplateEngineTemplateVersion>
|
<TemplateEngineTemplateVersion>1.0.0-beta2-20170727-301</TemplateEngineTemplateVersion>
|
||||||
<TemplateEngineTemplate2_0Version>1.0.0-beta2-20170725-300</TemplateEngineTemplate2_0Version>
|
<TemplateEngineTemplate2_0Version>1.0.0-beta2-20170727-301</TemplateEngineTemplate2_0Version>
|
||||||
<PlatformAbstractionsVersion>2.0.0</PlatformAbstractionsVersion>
|
<PlatformAbstractionsVersion>2.0.0</PlatformAbstractionsVersion>
|
||||||
<DependencyModelVersion>2.0.0</DependencyModelVersion>
|
<DependencyModelVersion>2.0.0</DependencyModelVersion>
|
||||||
<CliCommandLineParserVersion>0.1.1-alpha-167</CliCommandLineParserVersion>
|
<CliCommandLineParserVersion>0.1.1-alpha-167</CliCommandLineParserVersion>
|
||||||
<CliMigrateVersion>1.2.1-alpha-002133</CliMigrateVersion>
|
<CliMigrateVersion>1.2.1-alpha-002133</CliMigrateVersion>
|
||||||
<MicroBuildVersion>0.2.0</MicroBuildVersion>
|
<MicroBuildVersion>0.2.0</MicroBuildVersion>
|
||||||
<SpaTemplateVersion>1.0.0-preview-000409</SpaTemplateVersion>
|
<SpaTemplateVersion>1.0.417</SpaTemplateVersion>
|
||||||
<XliffTasksVersion>0.2.0-beta-000042</XliffTasksVersion>
|
<XliffTasksVersion>0.2.0-beta-000042</XliffTasksVersion>
|
||||||
|
|
||||||
<!-- This should either be timestamped or notimestamp as appropriate -->
|
<!-- This should either be timestamped or notimestamp as appropriate -->
|
||||||
|
|
|
@ -43,13 +43,15 @@
|
||||||
SkipUnchangedFiles="False"
|
SkipUnchangedFiles="False"
|
||||||
UseHardlinksIfPossible="False" />
|
UseHardlinksIfPossible="False" />
|
||||||
|
|
||||||
<!-- Create layout: postinst -->
|
<!-- Create layout: Generate and Place postinst -->
|
||||||
<Copy
|
<ReplaceFileContents
|
||||||
DestinationFiles= "$(DebianPostinstFile)"
|
InputFile="$(DebianPostinstTemplateFile)"
|
||||||
SourceFiles="$(DebianPostinstTemplateFile)"
|
DestinationFile="$(DebianPostinstFile)"
|
||||||
OverwriteReadOnlyFiles="True"
|
ReplacementItems="@(DebianPostInstTokenValues)" />
|
||||||
SkipUnchangedFiles="False"
|
|
||||||
UseHardlinksIfPossible="False" />
|
<Chmod
|
||||||
|
Glob="$(DebianPostinstFile)"
|
||||||
|
Mode="ugo+x" />
|
||||||
|
|
||||||
<!-- Create layout: Generate and Place debian_config.json -->
|
<!-- Create layout: Generate and Place debian_config.json -->
|
||||||
<ReplaceFileContents
|
<ReplaceFileContents
|
||||||
|
|
|
@ -36,9 +36,10 @@
|
||||||
<HostFxrDebianPackageName>$(HostFxrDebianPackageName.ToLower())</HostFxrDebianPackageName>
|
<HostFxrDebianPackageName>$(HostFxrDebianPackageName.ToLower())</HostFxrDebianPackageName>
|
||||||
<HostDebianPackageName>dotnet-host</HostDebianPackageName>
|
<HostDebianPackageName>dotnet-host</HostDebianPackageName>
|
||||||
<AspNetCoreRuntimePackageName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageName>
|
<AspNetCoreRuntimePackageName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageName>
|
||||||
|
<AspNetCoreRuntimePackageName Condition=" '$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp' ">$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)</AspNetCoreRuntimePackageName>
|
||||||
<HostRidInAspNetCoreRuntimeDebInstallerFileName>$(HostRid)</HostRidInAspNetCoreRuntimeDebInstallerFileName>
|
<HostRidInAspNetCoreRuntimeDebInstallerFileName>$(HostRid)</HostRidInAspNetCoreRuntimeDebInstallerFileName>
|
||||||
<HostRidInAspNetCoreRuntimeDebInstallerFileName Condition=" '$(HostRid)' == 'debian.8-x64' ">debian-x64</HostRidInAspNetCoreRuntimeDebInstallerFileName>
|
|
||||||
<AspNetCoreRuntimeDebInstallerFileName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeDebInstallerFileName).deb</AspNetCoreRuntimeDebInstallerFileName>
|
<AspNetCoreRuntimeDebInstallerFileName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeDebInstallerFileName).deb</AspNetCoreRuntimeDebInstallerFileName>
|
||||||
|
<AspNetCoreRuntimeDebInstallerFileName Condition=" '$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp' ">$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)-$(HostRidInAspNetCoreRuntimeDebInstallerFileName).deb</AspNetCoreRuntimeDebInstallerFileName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- Inputs -->
|
<!-- Inputs -->
|
||||||
|
@ -105,6 +106,10 @@
|
||||||
<DebianConfigTokenValues Include="%CLI_SDK_BRAND_NAME%">
|
<DebianConfigTokenValues Include="%CLI_SDK_BRAND_NAME%">
|
||||||
<ReplacementString>$(SdkBrandName)</ReplacementString>
|
<ReplacementString>$(SdkBrandName)</ReplacementString>
|
||||||
</DebianConfigTokenValues>
|
</DebianConfigTokenValues>
|
||||||
|
|
||||||
|
<DebianPostInstTokenValues Include="%SDK_VERSION%">
|
||||||
|
<ReplacementString>$(SdkVersion)</ReplacementString>
|
||||||
|
</DebianPostInstTokenValues>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
|
|
||||||
<SdkPkgSourcesRootDirectory>$(RepoRoot)/packaging/osx/clisdk</SdkPkgSourcesRootDirectory>
|
<SdkPkgSourcesRootDirectory>$(RepoRoot)/packaging/osx/clisdk</SdkPkgSourcesRootDirectory>
|
||||||
<SdkPkgScriptsDirectory>$(SdkPkgSourcesRootDirectory)/scripts</SdkPkgScriptsDirectory>
|
<SdkPkgScriptsDirectory>$(SdkPkgSourcesRootDirectory)/scripts</SdkPkgScriptsDirectory>
|
||||||
|
<SdkPkgScriptTemplateFile>$(SdkPkgScriptsDirectory)/postinstall</SdkPkgScriptTemplateFile>
|
||||||
|
<SdkPkgDestinationScriptsDirectory>$(PkgIntermediateDirectory)/scripts</SdkPkgDestinationScriptsDirectory>
|
||||||
|
<SdkPkgScriptFile>$(SdkPkgDestinationScriptsDirectory)/postinstall</SdkPkgScriptFile>
|
||||||
<SdkProductArchiveResourcesDirectory>$(SdkPkgSourcesRootDirectory)/resources</SdkProductArchiveResourcesDirectory>
|
<SdkProductArchiveResourcesDirectory>$(SdkPkgSourcesRootDirectory)/resources</SdkProductArchiveResourcesDirectory>
|
||||||
|
|
||||||
<SdkProductArchiveDistributionTemplateFile>$(SdkPkgSourcesRootDirectory)/Distribution-Template</SdkProductArchiveDistributionTemplateFile>
|
<SdkProductArchiveDistributionTemplateFile>$(SdkPkgSourcesRootDirectory)/Distribution-Template</SdkProductArchiveDistributionTemplateFile>
|
||||||
|
@ -54,6 +57,10 @@
|
||||||
<DistributionTemplateReplacement Include="{HostFxrBrandName}">
|
<DistributionTemplateReplacement Include="{HostFxrBrandName}">
|
||||||
<ReplacementString>$(HostFxrBrandName)</ReplacementString>
|
<ReplacementString>$(HostFxrBrandName)</ReplacementString>
|
||||||
</DistributionTemplateReplacement>
|
</DistributionTemplateReplacement>
|
||||||
|
|
||||||
|
<PostInstallScriptReplacement Include="%SDK_VERSION%">
|
||||||
|
<ReplacementString>$(SdkVersion)</ReplacementString>
|
||||||
|
</PostInstallScriptReplacement>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Consumed By Publish -->
|
<!-- Consumed By Publish -->
|
||||||
|
@ -83,12 +90,22 @@
|
||||||
<Copy SourceFiles="@(AspNetRuntimeFilesInput)"
|
<Copy SourceFiles="@(AspNetRuntimeFilesInput)"
|
||||||
DestinationFiles="@(AspNetRuntimeFilesInput->'$(SdkLayoutOutputDirectory)/%(RecursiveDir)%(FileName)%(Extension)')" />
|
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
|
<Exec Command="pkgbuild
|
||||||
--root '$(SdkLayoutOutputDirectory)'
|
--root '$(SdkLayoutOutputDirectory)'
|
||||||
--identifier '$(SdkComponentId)'
|
--identifier '$(SdkComponentId)'
|
||||||
--version '$(SdkVersion)'
|
--version '$(SdkVersion)'
|
||||||
--install-location '$(PkgInstallDirectory)'
|
--install-location '$(PkgInstallDirectory)'
|
||||||
--scripts '$(SdkPkgScriptsDirectory)'
|
--scripts '$(SdkPkgDestinationScriptsDirectory)'
|
||||||
'$(SdkInstallerFile)'" />
|
'$(SdkInstallerFile)'" />
|
||||||
</Target>
|
</Target>
|
||||||
|
|
||||||
|
|
|
@ -4,5 +4,6 @@
|
||||||
<RpmConfigJsonName>rpm_config.json</RpmConfigJsonName>
|
<RpmConfigJsonName>rpm_config.json</RpmConfigJsonName>
|
||||||
<TemplatesDir>$(RepoRoot)/packaging/rpm/templates</TemplatesDir>
|
<TemplatesDir>$(RepoRoot)/packaging/rpm/templates</TemplatesDir>
|
||||||
<ScriptsDir>$(RepoRoot)/packaging/rpm/scripts</ScriptsDir>
|
<ScriptsDir>$(RepoRoot)/packaging/rpm/scripts</ScriptsDir>
|
||||||
|
<AfterInstallHostScriptName>after_install_host.sh</AfterInstallHostScriptName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -51,10 +51,14 @@
|
||||||
<HostFxrRpmPackageName>$(HostFxrRpmPackageName.ToLower())</HostFxrRpmPackageName>
|
<HostFxrRpmPackageName>$(HostFxrRpmPackageName.ToLower())</HostFxrRpmPackageName>
|
||||||
<HostRpmPackageName>dotnet-host</HostRpmPackageName>
|
<HostRpmPackageName>dotnet-host</HostRpmPackageName>
|
||||||
<HostRidInAspNetCoreRuntimeRpmInstallerFileName>$(HostRid)</HostRidInAspNetCoreRuntimeRpmInstallerFileName>
|
<HostRidInAspNetCoreRuntimeRpmInstallerFileName>$(HostRid)</HostRidInAspNetCoreRuntimeRpmInstallerFileName>
|
||||||
<HostRidInAspNetCoreRuntimeRpmInstallerFileName Condition=" '$(HostRid)' == 'rhel.7-x64' ">rhel-x64</HostRidInAspNetCoreRuntimeRpmInstallerFileName>
|
|
||||||
<AspNetCoreRuntimePackageName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageName>
|
<AspNetCoreRuntimePackageName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageName>
|
||||||
|
<AspNetCoreRuntimePackageName Condition="'$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp'">$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersion)</AspNetCoreRuntimePackageName>
|
||||||
<AspNetCoreRuntimePackageVersion>$(AspNetCoreVersion)-$(AspNetCoreRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageVersion>
|
<AspNetCoreRuntimePackageVersion>$(AspNetCoreVersion)-$(AspNetCoreRelease)-$(AspNetCoreRuntimePackageTimestamp)</AspNetCoreRuntimePackageVersion>
|
||||||
|
<AspNetCoreRuntimePackageVersion Condition="'$(AspNetCoreRuntimePackageFlavor)' == 'notimestamp'">$(AspNetCoreVersion)</AspNetCoreRuntimePackageVersion>
|
||||||
<AspNetCoreRuntimeRpmInstallerFileName>$(AspNetCoreRuntimePackageBrandName)-$(AspNetCoreVersionAndRelease)-$(AspNetCoreRuntimePackageTimestamp)-$(HostRidInAspNetCoreRuntimeRpmInstallerFileName).rpm</AspNetCoreRuntimeRpmInstallerFileName>
|
<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>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
@ -78,7 +82,7 @@
|
||||||
<SDKFiles Include="$(InputRoot)/**/*"/>
|
<SDKFiles Include="$(InputRoot)/**/*"/>
|
||||||
<SDKManpages Include="$(ManPagesDir)/**/*"/>
|
<SDKManpages Include="$(ManPagesDir)/**/*"/>
|
||||||
<SDKTemplatesFiles Include="$(TemplatesDir)/**/*"/>
|
<SDKTemplatesFiles Include="$(TemplatesDir)/**/*"/>
|
||||||
<SDKScriptsFiles Include="$(ScriptsDir)/**/*"/>
|
<SDKScriptsFiles Include="$(ScriptsDir)/after_remove_host.sh"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<Copy SourceFiles="@(SDKFiles)"
|
<Copy SourceFiles="@(SDKFiles)"
|
||||||
|
@ -128,6 +132,10 @@
|
||||||
<SDKTokenValue Include="%SDK_RPM_PACKAGE_NAME%">
|
<SDKTokenValue Include="%SDK_RPM_PACKAGE_NAME%">
|
||||||
<ReplacementString>$(SdkRpmPackageName)</ReplacementString>
|
<ReplacementString>$(SdkRpmPackageName)</ReplacementString>
|
||||||
</SDKTokenValue>
|
</SDKTokenValue>
|
||||||
|
|
||||||
|
<AfterInstallHostTokenValue Include="%SDK_VERSION%">
|
||||||
|
<ReplacementString>$(SdkVersion)</ReplacementString>
|
||||||
|
</AfterInstallHostTokenValue>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -135,6 +143,14 @@
|
||||||
<GeneratedInstallers Include="$(SdkInstallerFile)" />
|
<GeneratedInstallers Include="$(SdkInstallerFile)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ReplaceFileContents InputFile="$(AfterInstallHostScriptTemplateFile)"
|
||||||
|
DestinationFile="$(AfterInstallHostScriptDestinationFile)"
|
||||||
|
ReplacementItems="@(AfterInstallHostTokenValue)"/>
|
||||||
|
|
||||||
|
<Chmod
|
||||||
|
Glob="$(AfterInstallHostScriptDestinationFile)"
|
||||||
|
Mode="ugo+x" />
|
||||||
|
|
||||||
<ReplaceFileContents InputFile="$(ConfigJsonFile)"
|
<ReplaceFileContents InputFile="$(ConfigJsonFile)"
|
||||||
DestinationFile="$(RpmLayoutDirectory)$(RpmConfigJsonName)"
|
DestinationFile="$(RpmLayoutDirectory)$(RpmConfigJsonName)"
|
||||||
ReplacementItems="@(SDKTokenValue)"/>
|
ReplacementItems="@(SDKTokenValue)"/>
|
||||||
|
@ -229,4 +245,4 @@
|
||||||
<Exec Command="sudo yum remove -y $(HostFxrRpmPackageName)" />
|
<Exec Command="sudo yum remove -y $(HostFxrRpmPackageName)" />
|
||||||
<Exec Command="sudo yum remove -y $(HostRpmPackageName)" />
|
<Exec Command="sudo yum remove -y $(HostRpmPackageName)" />
|
||||||
</Target>
|
</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."
|
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>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -13,6 +13,13 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
<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">
|
<div align="left" style="font-family: Helvetica">
|
||||||
<h2 style="padding-left:10px"> Resources </h2>
|
<h2 style="padding-left:10px"> Resources </h2>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -20,7 +27,6 @@
|
||||||
<li><a href="https://aka.ms/dotnet-cli-docs">SDK Documentation</a></li>
|
<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/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-tutorials">Tutorials</a></li>
|
||||||
<li><a href="https://aka.ms/dotnet-cli-telemetry">.NET Core Telemetry</a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -11,6 +11,6 @@ INSTALL_DESTINATION=$2
|
||||||
# A temporary fix for the permissions issue(s)
|
# A temporary fix for the permissions issue(s)
|
||||||
chmod -R 755 $INSTALL_DESTINATION
|
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
|
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."
|
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
|
• .NET Core Runtime 2.0.0
|
||||||
• Runtime Package Store
|
• 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
|
• Core Documentation https://aka.ms/dotnet-docs
|
||||||
• SDK Documentation https://aka.ms/dotnet-cli-docs
|
• SDK Documentation https://aka.ms/dotnet-cli-docs
|
||||||
• Release Notes https://aka.ms/20-p2-rel-notes
|
• Release Notes https://aka.ms/20-p2-rel-notes
|
||||||
• Tutorials https://aka.ms/dotnet-tutorials
|
• Tutorials https://aka.ms/dotnet-tutorials</String>
|
||||||
• .NET Core Telemetry https://aka.ms/dotnet-cli-telemetry</String>
|
|
||||||
<String Id="WelcomeHeaderMessage">.NET Core SDK</String>
|
<String Id="WelcomeHeaderMessage">.NET Core SDK</String>
|
||||||
<String Id="WelcomeDescription">
|
<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>
|
.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="ProductCPU" Value="$(var.Platform)" />
|
||||||
<Property Id="RTM_ProductVersion" Value="$(var.Dotnet_ProductVersion)" />
|
<Property Id="RTM_ProductVersion" Value="$(var.Dotnet_ProductVersion)" />
|
||||||
<Property Id="MSIFASTINSTALL" Value="7" />
|
<Property Id="MSIFASTINSTALL" Value="7" />
|
||||||
|
<Property Id="NUGETVERSION" Value="$(var.NugetVersion)" />
|
||||||
<WixVariable Id="WixUILicenseRtf" Value="$(var.MicrosoftEula)" />
|
<WixVariable Id="WixUILicenseRtf" Value="$(var.MicrosoftEula)" />
|
||||||
|
|
||||||
<Property Id="DOTNETEXE">
|
<Property Id="DOTNETEXE">
|
||||||
|
@ -32,7 +33,7 @@
|
||||||
|
|
||||||
<CustomAction Id="PropertyAssignPrimeCacheAndTelemetry"
|
<CustomAction Id="PropertyAssignPrimeCacheAndTelemetry"
|
||||||
Property="QtExecPrimeCacheAndTelemetryTarget"
|
Property="QtExecPrimeCacheAndTelemetryTarget"
|
||||||
Value=""[DOTNETHOME]\dotnet.exe" internal-reportinstallsuccess "[EXEFULLPATH]""
|
Value=""[DOTNETHOME]\dotnet.exe" exec "[DOTNETHOME]\sdk\[NUGETVERSION]\dotnet.dll" internal-reportinstallsuccess "[EXEFULLPATH]""
|
||||||
Execute="immediate" />
|
Execute="immediate" />
|
||||||
<CustomAction Id="QtExecPrimeCacheAndTelemetryTarget"
|
<CustomAction Id="QtExecPrimeCacheAndTelemetryTarget"
|
||||||
BinaryKey="WixCA"
|
BinaryKey="WixCA"
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Rozbalení</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Dekomprese</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Aufklappen</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Dekomprimieren</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Expandiendo</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Descomprimiendo</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Développement</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Décompression</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Espansione</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Decompressione</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">展開中</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">圧縮解除中</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">확장하는 중</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">압축을 푸는 중</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Rozwijanie</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Dekompresja</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Expandindo</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Descompactando</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Идет расширение</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Идет извлечение</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">Genişletme</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">Daraltma</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">正在扩展</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">正在解压缩</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
<body>
|
<body>
|
||||||
<trans-unit id="Expanding">
|
<trans-unit id="Expanding">
|
||||||
<source>Expanding</source>
|
<source>Expanding</source>
|
||||||
<target state="new">Expanding</target>
|
<target state="translated">正在展開</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Decompressing">
|
<trans-unit id="Decompressing">
|
||||||
<source>Decompressing</source>
|
<source>Decompressing</source>
|
||||||
<target state="new">Decompressing</target>
|
<target state="translated">正在解壓縮</target>
|
||||||
<note />
|
<note />
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</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();
|
PrintUnauthorizedAccessMessage();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrintNugetCachePrimeMessage();
|
||||||
|
|
||||||
PrintNugetCachePrimeMessage();
|
_nugetCachePrimer.PrimeCache();
|
||||||
|
}
|
||||||
_nugetCachePrimer.PrimeCache();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +83,8 @@ namespace Microsoft.DotNet.Configurer
|
||||||
private bool ShouldPrimeNugetCache()
|
private bool ShouldPrimeNugetCache()
|
||||||
{
|
{
|
||||||
return ShouldRunFirstRunExperience() &&
|
return ShouldRunFirstRunExperience() &&
|
||||||
|
!_nugetCacheSentinel.Exists() &&
|
||||||
|
!_nugetCacheSentinel.InProgressSentinelAlreadyExists() &&
|
||||||
!_nugetCachePrimer.SkipPrimingTheCache();
|
!_nugetCachePrimer.SkipPrimingTheCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,9 +100,7 @@ namespace Microsoft.DotNet.Configurer
|
||||||
var skipFirstTimeExperience =
|
var skipFirstTimeExperience =
|
||||||
_environmentProvider.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false);
|
_environmentProvider.GetEnvironmentVariableAsBool("DOTNET_SKIP_FIRST_TIME_EXPERIENCE", false);
|
||||||
|
|
||||||
return !skipFirstTimeExperience &&
|
return !skipFirstTimeExperience;
|
||||||
!_nugetCacheSentinel.Exists() &&
|
|
||||||
!_nugetCacheSentinel.InProgressSentinelAlreadyExists();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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()
|
public bool InProgressSentinelAlreadyExists()
|
||||||
{
|
{
|
||||||
return CouldNotGetAHandleToTheInProgressSentinel();
|
return CouldNotGetAHandleToTheInProgressSentinel() && !UnauthorizedAccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool Exists()
|
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.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using Microsoft.DotNet.Cli.CommandLine;
|
using Microsoft.DotNet.Cli.CommandLine;
|
||||||
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
using Microsoft.DotNet.PlatformAbstractions;
|
using Microsoft.DotNet.PlatformAbstractions;
|
||||||
|
@ -80,8 +82,10 @@ namespace Microsoft.DotNet.Cli
|
||||||
var lastArg = 0;
|
var lastArg = 0;
|
||||||
var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator();
|
var cliFallbackFolderPathCalculator = new CliFallbackFolderPathCalculator();
|
||||||
using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator))
|
using (INuGetCacheSentinel nugetCacheSentinel = new NuGetCacheSentinel(cliFallbackFolderPathCalculator))
|
||||||
using (IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator))
|
using (IFirstTimeUseNoticeSentinel disposableFirstTimeUseNoticeSentinel =
|
||||||
|
new FirstTimeUseNoticeSentinel(cliFallbackFolderPathCalculator))
|
||||||
{
|
{
|
||||||
|
IFirstTimeUseNoticeSentinel firstTimeUseNoticeSentinel = disposableFirstTimeUseNoticeSentinel;
|
||||||
for (; lastArg < args.Length; lastArg++)
|
for (; lastArg < args.Length; lastArg++)
|
||||||
{
|
{
|
||||||
if (IsArg(args[lastArg], "d", "diagnostics"))
|
if (IsArg(args[lastArg], "d", "diagnostics"))
|
||||||
|
@ -100,8 +104,8 @@ namespace Microsoft.DotNet.Cli
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (IsArg(args[lastArg], "h", "help") ||
|
else if (IsArg(args[lastArg], "h", "help") ||
|
||||||
args[lastArg] == "-?" ||
|
args[lastArg] == "-?" ||
|
||||||
args[lastArg] == "/?")
|
args[lastArg] == "/?")
|
||||||
{
|
{
|
||||||
HelpCommand.PrintHelp();
|
HelpCommand.PrintHelp();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -113,10 +117,19 @@ namespace Microsoft.DotNet.Cli
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ConfigureDotNetForFirstTimeUse(nugetCacheSentinel, firstTimeUseNoticeSentinel, cliFallbackFolderPathCalculator);
|
|
||||||
|
|
||||||
// It's the command, and we're done!
|
// It's the command, and we're done!
|
||||||
command = args[lastArg];
|
command = args[lastArg];
|
||||||
|
|
||||||
|
if (IsDotnetBeingInvokedFromNativeInstaller(command))
|
||||||
|
{
|
||||||
|
firstTimeUseNoticeSentinel = new NoOpFirstTimeUseNoticeSentinel();
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigureDotNetForFirstTimeUse(
|
||||||
|
nugetCacheSentinel,
|
||||||
|
firstTimeUseNoticeSentinel,
|
||||||
|
cliFallbackFolderPathCalculator);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,11 +141,16 @@ namespace Microsoft.DotNet.Cli
|
||||||
|
|
||||||
if (telemetryClient == null)
|
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())
|
if (CommandContext.IsVerbose())
|
||||||
{
|
{
|
||||||
|
@ -144,12 +162,12 @@ namespace Microsoft.DotNet.Cli
|
||||||
command = "help";
|
command = "help";
|
||||||
}
|
}
|
||||||
|
|
||||||
telemetryClient.TrackEvent(command, null, null);
|
TelemetryEventEntry.TrackEvent(command, null, null);
|
||||||
|
|
||||||
int exitCode;
|
int exitCode;
|
||||||
BuiltInCommandMetadata builtIn;
|
if (BuiltInCommandsCatalog.Commands.TryGetValue(command, out var builtIn))
|
||||||
if (BuiltInCommandsCatalog.Commands.TryGetValue(command, out builtIn))
|
|
||||||
{
|
{
|
||||||
|
TelemetryEventEntry.SendFiltered(Parser.Instance.ParseFrom($"dotnet {command}", appArgs.ToArray()));
|
||||||
exitCode = builtIn.Command(appArgs.ToArray());
|
exitCode = builtIn.Command(appArgs.ToArray());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -161,9 +179,12 @@ namespace Microsoft.DotNet.Cli
|
||||||
.Execute();
|
.Execute();
|
||||||
exitCode = result.ExitCode;
|
exitCode = result.ExitCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
return exitCode;
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsDotnetBeingInvokedFromNativeInstaller(string command)
|
||||||
|
{
|
||||||
|
return command == "internal-reportinstallsuccess";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ConfigureDotNetForFirstTimeUse(
|
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.
|
// 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.
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli
|
namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
{
|
{
|
||||||
public interface ITelemetry
|
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;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.ApplicationInsights;
|
using Microsoft.ApplicationInsights;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
using Microsoft.DotNet.PlatformAbstractions;
|
using Microsoft.DotNet.PlatformAbstractions;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli
|
namespace Microsoft.DotNet.Cli.Telemetry
|
||||||
{
|
{
|
||||||
public class Telemetry : ITelemetry
|
public class Telemetry : ITelemetry
|
||||||
{
|
{
|
||||||
internal static string CurrentSessionId = null;
|
internal static string CurrentSessionId = null;
|
||||||
private TelemetryClient _client = null;
|
private TelemetryClient _client = null;
|
||||||
|
|
||||||
private Dictionary<string, string> _commonProperties = null;
|
private Dictionary<string, string> _commonProperties = null;
|
||||||
private Dictionary<string, double> _commonMeasurements = null;
|
private Dictionary<string, double> _commonMeasurements = null;
|
||||||
private Task _trackEventTask = null;
|
private Task _trackEventTask = null;
|
||||||
|
|
||||||
private const string InstrumentationKey = "74cc1c9e-3e6e-4d05-b3fc-dde9101d0254";
|
private const string InstrumentationKey = "74cc1c9e-3e6e-4d05-b3fc-dde9101d0254";
|
||||||
private const string TelemetryOptout = "DOTNET_CLI_TELEMETRY_OPTOUT";
|
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 bool Enabled { get; }
|
||||||
|
|
||||||
public Telemetry () : this(null) { }
|
public Telemetry() : this(null) { }
|
||||||
|
|
||||||
public Telemetry(IFirstTimeUseNoticeSentinel sentinel) : this(sentinel, null) { }
|
public Telemetry(IFirstTimeUseNoticeSentinel sentinel) : this(sentinel, null) { }
|
||||||
|
|
||||||
public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId)
|
public Telemetry(IFirstTimeUseNoticeSentinel sentinel, string sessionId, bool blockThreadInitialization = false)
|
||||||
{
|
{
|
||||||
Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel);
|
Enabled = !Env.GetEnvironmentVariableAsBool(TelemetryOptout) && PermissionExists(sentinel);
|
||||||
|
|
||||||
|
@ -49,8 +41,15 @@ namespace Microsoft.DotNet.Cli
|
||||||
// Store the session ID in a static field so that it can be reused
|
// Store the session ID in a static field so that it can be reused
|
||||||
CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();
|
CurrentSessionId = sessionId ?? Guid.NewGuid().ToString();
|
||||||
|
|
||||||
//initialize in task to offload to parallel thread
|
if (blockThreadInitialization)
|
||||||
_trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry());
|
{
|
||||||
|
InitializeTelemetry();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//initialize in task to offload to parallel thread
|
||||||
|
_trackEventTask = Task.Factory.StartNew(() => InitializeTelemetry());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool PermissionExists(IFirstTimeUseNoticeSentinel sentinel)
|
private bool PermissionExists(IFirstTimeUseNoticeSentinel sentinel)
|
||||||
|
@ -63,7 +62,8 @@ namespace Microsoft.DotNet.Cli
|
||||||
return sentinel.Exists();
|
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)
|
if (!Enabled)
|
||||||
{
|
{
|
||||||
|
@ -92,26 +92,23 @@ namespace Microsoft.DotNet.Cli
|
||||||
_client = new TelemetryClient();
|
_client = new TelemetryClient();
|
||||||
_client.InstrumentationKey = InstrumentationKey;
|
_client.InstrumentationKey = InstrumentationKey;
|
||||||
_client.Context.Session.Id = CurrentSessionId;
|
_client.Context.Session.Id = CurrentSessionId;
|
||||||
|
|
||||||
_client.Context.Device.OperatingSystem = RuntimeEnvironment.OperatingSystem;
|
_client.Context.Device.OperatingSystem = RuntimeEnvironment.OperatingSystem;
|
||||||
|
|
||||||
_commonProperties = new Dictionary<string, string>();
|
_commonProperties = new TelemetryCommonProperties().GetTelemetryCommonProperties();
|
||||||
_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));
|
|
||||||
_commonMeasurements = new Dictionary<string, double>();
|
_commonMeasurements = new Dictionary<string, double>();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
_client = null;
|
_client = null;
|
||||||
// we dont want to fail the tool if telemetry fails.
|
// 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)
|
if (_client == null)
|
||||||
{
|
{
|
||||||
|
@ -120,15 +117,15 @@ namespace Microsoft.DotNet.Cli
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var eventProperties = GetEventProperties(properties);
|
Dictionary<string, string> eventProperties = GetEventProperties(properties);
|
||||||
var eventMeasurements = GetEventMeasures(measurements);
|
Dictionary<string, double> eventMeasurements = GetEventMeasures(measurements);
|
||||||
|
|
||||||
_client.TrackEvent(eventName, eventProperties, eventMeasurements);
|
_client.TrackEvent(eventName, eventProperties, eventMeasurements);
|
||||||
_client.Flush();
|
_client.Flush();
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Debug.Fail("Exception during TrackEventTask");
|
Debug.Fail(e.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,7 +134,7 @@ namespace Microsoft.DotNet.Cli
|
||||||
Dictionary<string, double> eventMeasurements = new Dictionary<string, double>(_commonMeasurements);
|
Dictionary<string, double> eventMeasurements = new Dictionary<string, double>(_commonMeasurements);
|
||||||
if (measurements != null)
|
if (measurements != null)
|
||||||
{
|
{
|
||||||
foreach (var measurement in measurements)
|
foreach (KeyValuePair<string, double> measurement in measurements)
|
||||||
{
|
{
|
||||||
if (eventMeasurements.ContainsKey(measurement.Key))
|
if (eventMeasurements.ContainsKey(measurement.Key))
|
||||||
{
|
{
|
||||||
|
@ -157,7 +154,7 @@ namespace Microsoft.DotNet.Cli
|
||||||
if (properties != null)
|
if (properties != null)
|
||||||
{
|
{
|
||||||
var eventProperties = new Dictionary<string, string>(_commonProperties);
|
var eventProperties = new Dictionary<string, string>(_commonProperties);
|
||||||
foreach (var property in properties)
|
foreach (KeyValuePair<string, string> property in properties)
|
||||||
{
|
{
|
||||||
if (eventProperties.ContainsKey(property.Key))
|
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.IO;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Cli
|
namespace Microsoft.DotNet.Cli
|
||||||
{
|
{
|
||||||
|
@ -37,13 +38,13 @@ namespace Microsoft.DotNet.Cli
|
||||||
|
|
||||||
internal class ThreadBlockingTelemetry : ITelemetry
|
internal class ThreadBlockingTelemetry : ITelemetry
|
||||||
{
|
{
|
||||||
private Telemetry telemetry;
|
private Telemetry.Telemetry telemetry;
|
||||||
|
|
||||||
internal ThreadBlockingTelemetry()
|
internal ThreadBlockingTelemetry()
|
||||||
{
|
{
|
||||||
var sessionId =
|
var sessionId =
|
||||||
Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName);
|
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;
|
public bool Enabled => telemetry.Enabled;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System.Runtime.InteropServices;
|
||||||
using Microsoft.DotNet.Cli;
|
using Microsoft.DotNet.Cli;
|
||||||
using Microsoft.DotNet.Cli.CommandLine;
|
using Microsoft.DotNet.Cli.CommandLine;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Tools.MSBuild
|
namespace Microsoft.DotNet.Tools.MSBuild
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System;
|
||||||
using Microsoft.Build.Framework;
|
using Microsoft.Build.Framework;
|
||||||
using Microsoft.Build.Utilities;
|
using Microsoft.Build.Utilities;
|
||||||
using Microsoft.DotNet.Cli;
|
using Microsoft.DotNet.Cli;
|
||||||
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
|
|
||||||
namespace Microsoft.DotNet.Tools.MSBuild
|
namespace Microsoft.DotNet.Tools.MSBuild
|
||||||
|
@ -39,15 +40,13 @@ namespace Microsoft.DotNet.Tools.MSBuild
|
||||||
{
|
{
|
||||||
if (_telemetry != null && _telemetry.Enabled)
|
if (_telemetry != null && _telemetry.Enabled)
|
||||||
{
|
{
|
||||||
IEventSource2 eventSource2 = eventSource as IEventSource2;
|
if (eventSource is IEventSource2 eventSource2)
|
||||||
|
|
||||||
if (eventSource2 != null)
|
|
||||||
{
|
{
|
||||||
eventSource2.TelemetryLogged += OnTelemetryLogged;
|
eventSource2.TelemetryLogged += OnTelemetryLogged;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
// Exceptions during telemetry shouldn't cause anything else to fail
|
// Exceptions during telemetry shouldn't cause anything else to fail
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.DotNet.Cli;
|
using Microsoft.DotNet.Cli;
|
||||||
|
using Microsoft.DotNet.Cli.Telemetry;
|
||||||
using Microsoft.DotNet.Cli.Utils;
|
using Microsoft.DotNet.Cli.Utils;
|
||||||
using Microsoft.DotNet.Configurer;
|
using Microsoft.DotNet.Configurer;
|
||||||
using Microsoft.DotNet.Tools.MSBuild;
|
using Microsoft.DotNet.Tools.MSBuild;
|
||||||
|
|
|
@ -32,7 +32,7 @@ namespace Microsoft.DotNet.Tools.NuGet
|
||||||
|
|
||||||
private class NuGetCommandRunner : ICommandRunner
|
private class NuGetCommandRunner : ICommandRunner
|
||||||
{
|
{
|
||||||
public int Run(string [] args)
|
public int Run(string[] args)
|
||||||
{
|
{
|
||||||
var nugetApp = new NuGetForwardingApp(args);
|
var nugetApp = new NuGetForwardingApp(args);
|
||||||
|
|
||||||
|
|
|
@ -233,5 +233,73 @@ namespace Microsoft.DotNet.Configurer.UnitTests
|
||||||
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once);
|
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Once);
|
||||||
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
|
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void It_prints_the_first_time_use_notice_if_the_cache_sentinel_exists_but_the_first_notice_sentinel_does_not()
|
||||||
|
{
|
||||||
|
_nugetCacheSentinelMock.Setup(n => n.Exists()).Returns(true);
|
||||||
|
_firstTimeUseNoticeSentinelMock.Setup(n => n.Exists()).Returns(false);
|
||||||
|
|
||||||
|
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
|
||||||
|
_nugetCachePrimerMock.Object,
|
||||||
|
_nugetCacheSentinelMock.Object,
|
||||||
|
_firstTimeUseNoticeSentinelMock.Object,
|
||||||
|
_environmentProviderMock.Object,
|
||||||
|
_reporterMock.Object,
|
||||||
|
CliFallbackFolderPath);
|
||||||
|
|
||||||
|
dotnetFirstTimeUseConfigurer.Configure();
|
||||||
|
|
||||||
|
_reporterMock.Verify(r =>
|
||||||
|
r.WriteLine(It.Is<string>(str => str == LocalizableStrings.FirstTimeWelcomeMessage)));
|
||||||
|
_reporterMock.Verify(
|
||||||
|
r => r.WriteLine(It.Is<string>(str => str == LocalizableStrings.NugetCachePrimeMessage)),
|
||||||
|
Times.Never);
|
||||||
|
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void It_prints_the_unauthorized_notice_if_the_cache_sentinel_reports_Unauthorized()
|
||||||
|
{
|
||||||
|
_nugetCacheSentinelMock.Setup(n => n.UnauthorizedAccess).Returns(true);
|
||||||
|
|
||||||
|
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
|
||||||
|
_nugetCachePrimerMock.Object,
|
||||||
|
_nugetCacheSentinelMock.Object,
|
||||||
|
_firstTimeUseNoticeSentinelMock.Object,
|
||||||
|
_environmentProviderMock.Object,
|
||||||
|
_reporterMock.Object,
|
||||||
|
CliFallbackFolderPath);
|
||||||
|
|
||||||
|
dotnetFirstTimeUseConfigurer.Configure();
|
||||||
|
|
||||||
|
_reporterMock.Verify(r =>
|
||||||
|
r.WriteLine(It.Is<string>(str => str == LocalizableStrings.FirstTimeWelcomeMessage)));
|
||||||
|
_reporterMock.Verify(r =>
|
||||||
|
r.WriteLine(It.Is<string>(str =>
|
||||||
|
str == string.Format(LocalizableStrings.UnauthorizedAccessMessage, CliFallbackFolderPath))));
|
||||||
|
_reporterMock.Verify(
|
||||||
|
r => r.WriteLine(It.Is<string>(str => str == LocalizableStrings.NugetCachePrimeMessage)),
|
||||||
|
Times.Never);
|
||||||
|
_reporterMock.Verify(r => r.Write(It.IsAny<string>()), Times.Never);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void It_does_not_prime_the_cache_if_the_cache_sentinel_reports_Unauthorized()
|
||||||
|
{
|
||||||
|
_nugetCacheSentinelMock.Setup(n => n.UnauthorizedAccess).Returns(true);
|
||||||
|
|
||||||
|
var dotnetFirstTimeUseConfigurer = new DotnetFirstTimeUseConfigurer(
|
||||||
|
_nugetCachePrimerMock.Object,
|
||||||
|
_nugetCacheSentinelMock.Object,
|
||||||
|
_firstTimeUseNoticeSentinelMock.Object,
|
||||||
|
_environmentProviderMock.Object,
|
||||||
|
_reporterMock.Object,
|
||||||
|
CliFallbackFolderPath);
|
||||||
|
|
||||||
|
dotnetFirstTimeUseConfigurer.Configure();
|
||||||
|
|
||||||
|
_nugetCachePrimerMock.Verify(r => r.PrimeCache(), Times.Never);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
nugetCacheSentinel.InProgressSentinelAlreadyExists().Should().BeTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void It_returns_false_to_the_in_progress_sentinel_already_exists_when_it_fails_to_get_a_handle_to_it_but_it_failed_because_it_was_unauthorized()
|
||||||
|
{
|
||||||
|
var fileMock = new FileMock();
|
||||||
|
var directoryMock = new DirectoryMock();
|
||||||
|
fileMock.InProgressSentinel = null;
|
||||||
|
var nugetCacheSentinel =
|
||||||
|
new NuGetCacheSentinel(NUGET_CACHE_PATH, fileMock, directoryMock);
|
||||||
|
|
||||||
|
nugetCacheSentinel.InProgressSentinelAlreadyExists().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void It_returns_false_to_the_in_progress_sentinel_already_exists_when_it_succeeds_in_getting_a_handle_to_it()
|
public void It_returns_false_to_the_in_progress_sentinel_already_exists_when_it_succeeds_in_getting_a_handle_to_it()
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,7 @@ using MSBuildCommand = Microsoft.DotNet.Tools.Test.Utilities.MSBuildCommand;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
|
||||||
// There are tests which modify static Telemetry.CurrentSessionId and they cannot run in parallel
|
// There are tests which modify static Telemetry.CurrentSessionId and they cannot run in parallel
|
||||||
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
[assembly: CollectionBehavior(DisableTestParallelization = true)]
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests
|
||||||
[Fact]
|
[Fact]
|
||||||
public void WhenTelemetryIsEnabledTheLoggerIsAddedToTheCommandLine()
|
public void WhenTelemetryIsEnabledTheLoggerIsAddedToTheCommandLine()
|
||||||
{
|
{
|
||||||
Telemetry telemetry;
|
Telemetry.Telemetry telemetry;
|
||||||
string[] allArgs = GetArgsForMSBuild(() => true, out telemetry);
|
string[] allArgs = GetArgsForMSBuild(() => true, out telemetry);
|
||||||
// telemetry will still be disabled if environment variable is set
|
// telemetry will still be disabled if environment variable is set
|
||||||
if (telemetry.Enabled)
|
if (telemetry.Enabled)
|
||||||
|
@ -156,14 +157,15 @@ namespace Microsoft.DotNet.Cli.MSBuild.Tests
|
||||||
|
|
||||||
private string[] GetArgsForMSBuild(Func<bool> sentinelExists)
|
private string[] GetArgsForMSBuild(Func<bool> sentinelExists)
|
||||||
{
|
{
|
||||||
Telemetry telemetry;
|
Telemetry.Telemetry telemetry;
|
||||||
return GetArgsForMSBuild(sentinelExists, out 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>());
|
MSBuildForwardingApp msBuildForwardingApp = new MSBuildForwardingApp(Enumerable.Empty<string>());
|
||||||
|
|
||||||
|
|
|
@ -42,8 +42,7 @@ namespace Microsoft.DotNet.New.Tests
|
||||||
bool skipSpaWebpackSteps)
|
bool skipSpaWebpackSteps)
|
||||||
{
|
{
|
||||||
string rootPath = TestAssets.CreateTestDirectory(identifier: $"{language}_{projectType}").FullName;
|
string rootPath = TestAssets.CreateTestDirectory(identifier: $"{language}_{projectType}").FullName;
|
||||||
//This works around the SPA templates not currently supporting the "--no-restore" switch
|
string noRestoreDirective = "--no-restore";
|
||||||
string noRestoreDirective = skipSpaWebpackSteps ? "" : "--no-restore";
|
|
||||||
|
|
||||||
new TestCommand("dotnet") { WorkingDirectory = rootPath }
|
new TestCommand("dotnet") { WorkingDirectory = rootPath }
|
||||||
.Execute($"new {projectType} -lang {language} -o {rootPath} --debug:ephemeral-hive {noRestoreDirective}")
|
.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 _firstDotnetNonVerbUseCommandResult;
|
||||||
private static CommandResult _firstDotnetVerbUseCommandResult;
|
private static CommandResult _firstDotnetVerbUseCommandResult;
|
||||||
private static DirectoryInfo _nugetFallbackFolder;
|
private static DirectoryInfo _nugetFallbackFolder;
|
||||||
|
private static DirectoryInfo _dotDotnetFolder;
|
||||||
|
private static string _testDirectory;
|
||||||
|
|
||||||
static GivenThatTheUserIsRunningDotNetForTheFirstTime()
|
static GivenThatTheUserIsRunningDotNetForTheFirstTime()
|
||||||
{
|
{
|
||||||
var testDirectory = TestAssets.CreateTestDirectory("Dotnet_first_time_experience_tests");
|
_testDirectory = TestAssets.CreateTestDirectory("Dotnet_first_time_experience_tests").FullName;
|
||||||
var testNuGetHome = Path.Combine(testDirectory.FullName, "nuget_home");
|
var testNuGetHome = Path.Combine(_testDirectory, "nuget_home");
|
||||||
var cliTestFallbackFolder = Path.Combine(testNuGetHome, ".dotnet", "NuGetFallbackFolder");
|
var cliTestFallbackFolder = Path.Combine(testNuGetHome, ".dotnet", "NuGetFallbackFolder");
|
||||||
|
|
||||||
var command = new DotnetCommand()
|
var command = new DotnetCommand()
|
||||||
.WithWorkingDirectory(testDirectory);
|
.WithWorkingDirectory(_testDirectory);
|
||||||
command.Environment["HOME"] = testNuGetHome;
|
command.Environment["HOME"] = testNuGetHome;
|
||||||
command.Environment["USERPROFILE"] = testNuGetHome;
|
command.Environment["USERPROFILE"] = testNuGetHome;
|
||||||
command.Environment["APPDATA"] = testNuGetHome;
|
command.Environment["APPDATA"] = testNuGetHome;
|
||||||
|
@ -40,6 +42,7 @@ namespace Microsoft.DotNet.Tests
|
||||||
_firstDotnetVerbUseCommandResult = command.ExecuteWithCapturedOutput("new --debug:ephemeral-hive");
|
_firstDotnetVerbUseCommandResult = command.ExecuteWithCapturedOutput("new --debug:ephemeral-hive");
|
||||||
|
|
||||||
_nugetFallbackFolder = new DirectoryInfo(cliTestFallbackFolder);
|
_nugetFallbackFolder = new DirectoryInfo(cliTestFallbackFolder);
|
||||||
|
_dotDotnetFolder = new DirectoryInfo(Path.Combine(testNuGetHome, ".dotnet"));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -77,6 +80,61 @@ namespace Microsoft.DotNet.Tests
|
||||||
.HaveFile($"{GetDotnetVersion()}.dotnetSentinel");
|
.HaveFile($"{GetDotnetVersion()}.dotnetSentinel");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItCreatesAFirstUseSentinelFileUnderTheDotDotNetFolder()
|
||||||
|
{
|
||||||
|
_dotDotnetFolder
|
||||||
|
.Should()
|
||||||
|
.HaveFile($"{GetDotnetVersion()}.dotnetFirstUseSentinel");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ItDoesNotCreateAFirstUseSentinelFileUnderTheDotDotNetFolderWhenInternalReportInstallSuccessIsInvoked()
|
||||||
|
{
|
||||||
|
var emptyHome = Path.Combine(_testDirectory, "empty_home");
|
||||||
|
|
||||||
|
var command = new DotnetCommand()
|
||||||
|
.WithWorkingDirectory(_testDirectory);
|
||||||
|
command.Environment["HOME"] = emptyHome;
|
||||||
|
command.Environment["USERPROFILE"] = emptyHome;
|
||||||
|
command.Environment["APPDATA"] = emptyHome;
|
||||||
|
command.Environment["DOTNET_CLI_TEST_FALLBACKFOLDER"] = _nugetFallbackFolder.FullName;
|
||||||
|
command.Environment["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "";
|
||||||
|
// Disable to prevent the creation of the .dotnet folder by optimizationdata.
|
||||||
|
command.Environment["DOTNET_DISABLE_MULTICOREJIT"] = "true";
|
||||||
|
command.Environment["SkipInvalidConfigurations"] = "true";
|
||||||
|
|
||||||
|
command.ExecuteWithCapturedOutput("internal-reportinstallsuccess test").Should().Pass();
|
||||||
|
|
||||||
|
var 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]
|
[Fact]
|
||||||
public void ItRestoresTheNuGetPackagesToTheNuGetCacheFolder()
|
public void ItRestoresTheNuGetPackagesToTheNuGetCacheFolder()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,52 +1,229 @@
|
||||||
// Copyright (c) .NET Foundation and contributors. All rights reserved.
|
// 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.
|
// 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 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
|
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 string EventName { get; set; }
|
||||||
public IDictionary<string, string> Properties { get; set; }
|
public IDictionary<string, string> Properties { get; set; }
|
||||||
|
public TelemetryCommandTests()
|
||||||
public void TrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> measurements)
|
|
||||||
{
|
{
|
||||||
EventName = eventName;
|
_fakeTelemetry = new FakeRecordEventNameTelemetry();
|
||||||
Properties = properties;
|
TelemetryEventEntry.Subscribe(_fakeTelemetry.TrackEvent);
|
||||||
|
TelemetryEventEntry.TelemetryFilter = new TelemetryFilter();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public class TelemetryCommandTests : TestBase
|
|
||||||
{
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void TestProjectDependencyIsNotAvailableThroughDriver()
|
public void TopLevelCommandNameShouldBeSentToTelemetry()
|
||||||
{
|
{
|
||||||
MockTelemetry mockTelemetry = new MockTelemetry();
|
string[] args = {"help"};
|
||||||
string[] args = { "help" };
|
Cli.Program.ProcessArgs(args);
|
||||||
Microsoft.DotNet.Cli.Program.ProcessArgs(args, mockTelemetry);
|
_fakeTelemetry.LogEntries.Should().Contain(e => e.EventName == args[0]);
|
||||||
Assert.Equal(mockTelemetry.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]
|
[WindowsOnlyFact]
|
||||||
public void InternalreportinstallsuccessCommandCollectExeNameWithEventname()
|
public void InternalreportinstallsuccessCommandCollectExeNameWithEventname()
|
||||||
{
|
{
|
||||||
MockTelemetry mockTelemetry = new MockTelemetry();
|
FakeRecordEventNameTelemetry fakeTelemetry = new FakeRecordEventNameTelemetry();
|
||||||
string[] args = { "c:\\mypath\\dotnet-sdk-latest-win-x64.exe" };
|
string[] args = { "c:\\mypath\\dotnet-sdk-latest-win-x64.exe" };
|
||||||
|
|
||||||
InternalReportinstallsuccess.ProcessInputAndSendTelemetry(args, mockTelemetry);
|
InternalReportinstallsuccess.ProcessInputAndSendTelemetry(args, fakeTelemetry);
|
||||||
|
|
||||||
mockTelemetry.EventName.Should().Be("reportinstallsuccess");
|
fakeTelemetry
|
||||||
mockTelemetry.Properties["exeName"].Should().Be("dotnet-sdk-latest-win-x64.exe");
|
.LogEntries.Should()
|
||||||
|
.Contain(e => e.EventName == "reportinstallsuccess" && e.Properties.ContainsKey("exeName") &&
|
||||||
|
e.Properties["exeName"] == "dotnet-sdk-latest-win-x64.exe");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[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