Installer Success Reporting for Windows

Issue https://github.com/dotnet/cli/issues/7091

Add internal command dotnet internal-reportinstallsuccess. Before
Windows installer finishes, run this command instead of dotnet new. It
will trigger the first time experience as well as sending telemetry with
installer exe name.

This command blocks to ensure that the webservice call completes.
This commit is contained in:
William Li 2017-06-27 13:46:09 -07:00 committed by William Lee
parent 44cd1cf385
commit 191e3e3019
8 changed files with 119 additions and 5 deletions

View file

@ -37,6 +37,7 @@
</MsiPackage>
<MsiPackage SourceFile="$(var.CLISDKMsiSourcePath)">
<MsiProperty Name="DOTNETHOME" Value="[DOTNETHOME]" />
<MsiProperty Name="EXEFULLPATH" Value="[WixBundleOriginalSource]" />
</MsiPackage>
<?if $(var.Platform)=x86?>
<PackageGroupRef Id="PG_AspNetCorePackageStore_x86"/>

View file

@ -30,12 +30,20 @@
<CustomActionRef Id="WixBroadcastEnvironmentChange" />
<CustomAction Id="PropertyAssign" Property="QtExecDotnetnew" Value="&quot;[DOTNETHOME]\dotnet.exe&quot; new" Execute="immediate" />
<CustomAction Id="QtExecDotnetnew" BinaryKey="WixCA" DllEntry="$(var.WixQuietExec)" Execute="deferred" Return="ignore" Impersonate="no"/>
<CustomAction Id="PropertyAssignPrimeCacheAndTelemetry"
Property="QtExecPrimeCacheAndTelemetryTarget"
Value="&quot;[DOTNETHOME]\dotnet.exe&quot; internal-reportinstallsuccess &quot;[EXEFULLPATH]&quot;"
Execute="immediate" />
<CustomAction Id="QtExecPrimeCacheAndTelemetryTarget"
BinaryKey="WixCA"
DllEntry="$(var.WixQuietExec)"
Execute="deferred"
Return="ignore"
Impersonate="no"/>
<InstallExecuteSequence>
<Custom Action="PropertyAssign" Before="QtExecDotnetnew" />
<Custom Action="QtExecDotnetnew" Before="InstallFinalize" />
<Custom Action="PropertyAssignPrimeCacheAndTelemetry" Before="QtExecPrimeCacheAndTelemetryTarget" />
<Custom Action="QtExecPrimeCacheAndTelemetryTarget" Before="InstallFinalize" />
</InstallExecuteSequence>
</Product>
<Fragment>

View file

@ -143,6 +143,10 @@ namespace Microsoft.DotNet.Cli
["parse"] = new BuiltInCommandMetadata
{
Command = ParseCommand.Run
},
["internal-reportinstallsuccess"] = new BuiltInCommandMetadata
{
Command = InternalReportinstallsuccess.Run
}
};
}

View file

@ -54,6 +54,7 @@ namespace Microsoft.DotNet.Cli
Create.Command("msbuild", ""),
Create.Command("vstest", ""),
CompleteCommandParser.Complete(),
InternalReportinstallsuccessCommandParser.InternalReportinstallsuccess(),
CommonOptions.HelpOption(),
Create.Option("--info", ""),
Create.Option("-d", ""),

View file

@ -76,6 +76,15 @@ namespace Microsoft.DotNet.Cli
);
}
public void ThreadBlockingTrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> measurements)
{
if (!Enabled)
{
return;
}
TrackEventTask(eventName, properties, measurements);
}
private void InitializeTelemetry()
{
try

View file

@ -0,0 +1,56 @@
// 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.IO;
using System.Collections.Generic;
using Microsoft.DotNet.Configurer;
namespace Microsoft.DotNet.Cli
{
public class InternalReportinstallsuccess
{
internal const string TelemetrySessionIdEnvironmentVariableName = "DOTNET_CLI_TELEMETRY_SESSIONID";
public static int Run(string[] args)
{
var telemetry = new ThreadBlockingTelemetry();
ProcessInputAndSendTelemetry(args, telemetry);
return 0;
}
public static void ProcessInputAndSendTelemetry(string[] args, ITelemetry telemetry)
{
var parser = Parser.Instance;
var result = parser.ParseFrom("dotnet internal-reportinstallsuccess", args);
var internalReportinstallsuccess = result["dotnet"]["internal-reportinstallsuccess"];
var exeName = Path.GetFileName(internalReportinstallsuccess.Arguments.Single());
telemetry.TrackEvent(
"reportinstallsuccess",
new Dictionary<string, string> { { "exeName", exeName } },
new Dictionary<string, double>());
}
internal class ThreadBlockingTelemetry : ITelemetry
{
private Telemetry telemetry;
internal ThreadBlockingTelemetry()
{
var sessionId =
Environment.GetEnvironmentVariable(TelemetrySessionIdEnvironmentVariableName);
telemetry = new Telemetry(new FirstTimeUseNoticeSentinel(new CliFallbackFolderPathCalculator()), sessionId);
}
public bool Enabled => telemetry.Enabled;
public void TrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> measurements)
{
telemetry.ThreadBlockingTrackEvent(eventName, properties, measurements);
}
}
}
}

View file

@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Linq;
using Microsoft.DotNet.Cli.CommandLine;
namespace Microsoft.DotNet.Cli
{
internal static class InternalReportinstallsuccessCommandParser
{
public static Command InternalReportinstallsuccess() =>
Create.Command(
"internal-reportinstallsuccess", "internal only",
Accept.ExactlyOneArgument());
}
}

View file

@ -17,14 +17,15 @@ namespace Microsoft.DotNet.Tests
public bool Enabled { get; set; }
public string EventName { get; set; }
public IDictionary<string, string> Properties { get; set; }
public void TrackEvent(string eventName, IDictionary<string, string> properties, IDictionary<string, double> measurements)
{
EventName = eventName;
Properties = properties;
}
}
public class TelemetryCommandTests : TestBase
{
[Fact]
@ -35,5 +36,23 @@ namespace Microsoft.DotNet.Tests
Microsoft.DotNet.Cli.Program.ProcessArgs(args, mockTelemetry);
Assert.Equal(mockTelemetry.EventName, args[0]);
}
[Fact]
public void InternalreportinstallsuccessCommandCollectExeNameWithEventname()
{
MockTelemetry mockTelemetry = new MockTelemetry();
string[] args = { "c:\\mypath\\dotnet-sdk-latest-win-x64.exe" };
InternalReportinstallsuccess.ProcessInputAndSendTelemetry(args, mockTelemetry);
mockTelemetry.EventName.Should().Be("reportinstallsuccess");
mockTelemetry.Properties["exeName"].Should().Be("dotnet-sdk-latest-win-x64.exe");
}
[Fact]
public void InternalreportinstallsuccessCommandIsRegistedInBuiltIn()
{
BuiltInCommandsCatalog.Commands.Should().ContainKey("internal-reportinstallsuccess");
}
}
}