in src/NuGet.Clients/NuGet.PackageManagement.UI/Actions/UIActionEngine.cs [288:592]
private async Task PerformActionImplAsync(
IServiceBroker serviceBroker,
INuGetProjectManagerService projectManagerService,
INuGetUI uiService,
ResolveActionsAsync resolveActionsAsync,
NuGetProjectActionType operationType,
UserAction? userAction,
CancellationToken cancellationToken)
{
var status = NuGetOperationStatus.Succeeded;
var startTime = DateTimeOffset.Now;
var packageCount = 0;
var continueAfterPreview = true;
var acceptedLicense = true;
List<string>? removedPackages = null;
var existingPackages = new HashSet<Tuple<string, string, string?>>();
List<Tuple<string, string>>? addedPackages = null;
List<Tuple<string, string>>? updatedPackagesOld = null;
List<Tuple<string, string>>? updatedPackagesNew = null;
bool? packageToInstallWasTransitive = null;
// Enable granular level telemetry events for nuget ui operation
uiService.ProjectContext.OperationId = Guid.NewGuid();
Stopwatch packageEnumerationTime = new Stopwatch();
packageEnumerationTime.Start();
try
{
IServiceBroker sb = uiService.UIContext.ServiceBroker;
int projectsCount = uiService.Projects.Count();
IEnumerable<IPackageReferenceContextInfo>? installedPackages = null;
// collect the install state of the existing packages
foreach (IProjectContextInfo project in uiService.Projects) // only one project when PM UI is in project mode
{
if (projectsCount == 1 && userAction != null && !userAction.IsSolutionLevel && userAction.Action == NuGetProjectActionType.Install && project.ProjectStyle == ProjectModel.ProjectStyle.PackageReference && project.ProjectKind == NuGetProjectKind.PackageReference)
{
IInstalledAndTransitivePackages installedAndTransitives = await project.GetInstalledAndTransitivePackagesAsync(sb, cancellationToken);
installedPackages = installedAndTransitives.InstalledPackages;
packageToInstallWasTransitive = false;
string packageIdToInstall = VSTelemetryServiceUtility.NormalizePackageId(userAction.PackageId);
foreach (IPackageReferenceContextInfo transitivePackage in installedAndTransitives.TransitivePackages)
{
if (packageIdToInstall == VSTelemetryServiceUtility.NormalizePackageId(transitivePackage.Identity.Id))
{
packageToInstallWasTransitive = true;
break;
}
}
}
else
{
installedPackages = await project.GetInstalledPackagesAsync(sb, cancellationToken);
}
foreach (IPackageReferenceContextInfo package in installedPackages)
{
existingPackages.Add(CreatePackageTuple(package));
}
}
}
catch (Exception)
{
// don't teardown the process if we have a telemetry failure
}
var sourceMappingProvider = new PackageSourceMappingProvider(uiService.Settings);
IReadOnlyList<PackageSourceMappingSourceItem> existingPackageSourceMappingSourceItems = sourceMappingProvider.GetPackageSourceMappingItems();
packageEnumerationTime.Stop();
await _lockService.ExecuteNuGetOperationAsync(async () =>
{
int? countCreatedTopLevelSourceMappings = null;
int? countCreatedTransitiveSourceMappings = null;
try
{
uiService.BeginOperation();
using (INuGetProjectUpgraderService? projectUpgrader = await serviceBroker.GetProxyAsync<INuGetProjectUpgraderService>(
NuGetServices.ProjectUpgraderService,
cancellationToken))
{
bool isAcceptedFormat = await CheckPackageManagementFormatAsync(projectUpgrader, uiService, cancellationToken);
if (!isAcceptedFormat)
{
status = NuGetOperationStatus.Cancelled;
return;
}
}
TelemetryServiceUtility.StartOrResumeTimer();
IReadOnlyList<ProjectAction> actions = await resolveActionsAsync(projectManagerService);
IReadOnlyList<PreviewResult> results = await GetPreviewResultsAsync(projectManagerService, actions, userAction, uiService, cancellationToken);
if (results.Any(previewResult => previewResult.NuGetOperationStatus != NuGetOperationStatus.Succeeded))
{
status = NuGetOperationStatus.Failed;
}
if (operationType == NuGetProjectActionType.Uninstall)
{
// removed packages don't have version info
removedPackages = results.SelectMany(result => result.Deleted)
.Select(package => package.Id)
.Distinct()
.ToList();
packageCount = removedPackages.Count;
}
else
{
// log rich info about added packages
addedPackages = results.SelectMany(result => result.Added)
.Select(package => new Tuple<string, string>(package.Id, (package.Version == null ? "" : package.Version.ToNormalizedString())))
.Distinct()
.ToList();
var addCount = addedPackages.Count;
//updated packages can have an old and a new id.
updatedPackagesOld = results.SelectMany(result => result.Updated)
.Select(package => new Tuple<string, string>(package.Old.Id, (package.Old.Version == null ? "" : package.Old.Version.ToNormalizedString())))
.Distinct()
.ToList();
updatedPackagesNew = results.SelectMany(result => result.Updated)
.Select(package => new Tuple<string, string>(package.New.Id, (package.New.Version == null ? "" : package.New.Version.ToNormalizedString())))
.Distinct()
.ToList();
var updateCount = updatedPackagesNew.Count;
// update packages count
packageCount = addCount + updateCount;
if (updateCount > 0)
{
// set operation type to update when there are packages being updated
operationType = NuGetProjectActionType.Update;
}
}
if (status == NuGetOperationStatus.Failed)
{
return;
}
TelemetryServiceUtility.StopTimer();
// Show the preview window.
if (uiService.DisplayPreviewWindow)
{
bool shouldContinue = uiService.PromptForPreviewAcceptance(results);
if (!shouldContinue)
{
continueAfterPreview = false;
return;
}
}
TelemetryServiceUtility.StartOrResumeTimer();
// Show the license acceptance window.
bool accepted = await CheckLicenseAcceptanceAsync(uiService, results, cancellationToken);
TelemetryServiceUtility.StartOrResumeTimer();
if (!accepted)
{
acceptedLicense = false;
return;
}
// Warn about the fact that the "dotnet" TFM is deprecated.
if (uiService.DisplayDeprecatedFrameworkWindow)
{
bool shouldContinue = await ShouldContinueDueToDotnetDeprecationAsync(projectManagerService, uiService, cancellationToken);
TelemetryServiceUtility.StartOrResumeTimer();
if (!shouldContinue)
{
return;
}
}
if (!cancellationToken.IsCancellationRequested)
{
PreviewResult? sourceMappingPreviewResult = results.SingleOrDefault(result => result.NewSourceMappings != null);
PackageSourceMappingUtility.ConfigureNewPackageSourceMappings(
userAction,
sourceMappingPreviewResult,
sourceMappingProvider,
existingPackageSourceMappingSourceItems,
out countCreatedTopLevelSourceMappings,
out countCreatedTransitiveSourceMappings);
await projectManagerService.ExecuteActionsAsync(
actions,
cancellationToken);
string[] projectIds = actions
.Select(action => action.ProjectId)
.Distinct()
.ToArray();
uiService.UIContext.RaiseProjectActionsExecuted(projectIds);
}
else
{
status = NuGetOperationStatus.Cancelled;
}
}
catch (System.Net.Http.HttpRequestException ex)
{
status = NuGetOperationStatus.Failed;
if (ex.InnerException != null)
{
uiService.ShowError(ex.InnerException);
}
else
{
uiService.ShowError(ex);
}
}
catch (Exception ex)
{
status = NuGetOperationStatus.Failed;
uiService.ShowError(ex);
}
finally
{
TelemetryServiceUtility.StopTimer();
var duration = TelemetryServiceUtility.GetTimerElapsedTime();
uiService.ProjectContext.Log(MessageLevel.Info,
string.Format(CultureInfo.CurrentCulture, Resources.Operation_TotalTime, duration));
uiService.EndOperation();
// don't show "Succeeded" if we actually cancelled...
if ((!continueAfterPreview) || (!acceptedLicense))
{
if (status == NuGetOperationStatus.Succeeded)
{
status = NuGetOperationStatus.Cancelled;
}
}
var plc = new PackageLoadContext(isSolution: false, uiService.UIContext);
IReadOnlyCollection<string> frameworks = await plc.GetSupportedFrameworksAsync();
string[] projectIds = (await ProjectUtility.GetSortedProjectIdsAsync(
uiService.UIContext.ServiceBroker,
uiService.Projects,
cancellationToken)).ToArray();
var isPackageSourceMappingEnabled = existingPackageSourceMappingSourceItems.Count > 0;
var actionTelemetryEvent = new VSActionsTelemetryEvent(
uiService.ProjectContext.OperationId.ToString(),
projectIds,
operationType,
OperationSource.UI,
startTime,
status,
packageCount,
DateTimeOffset.Now,
duration.TotalSeconds,
isPackageSourceMappingEnabled);
var nuGetUI = uiService as NuGetUI;
AddUiActionEngineTelemetryProperties(
actionTelemetryEvent,
continueAfterPreview,
acceptedLicense,
userAction,
nuGetUI?.SelectedIndex,
nuGetUI?.RecommendedCount,
nuGetUI?.RecommendPackages,
nuGetUI?.RecommenderVersion,
nuGetUI?.TopLevelVulnerablePackagesCount ?? 0,
nuGetUI?.TopLevelVulnerablePackagesMaxSeverities?.ToList() ?? new List<int>(),
nuGetUI?.TransitiveVulnerablePackagesCount ?? 0,
nuGetUI?.TransitiveVulnerablePackagesMaxSeverities?.ToList() ?? new List<int>(),
existingPackages,
addedPackages,
removedPackages,
updatedPackagesOld,
updatedPackagesNew,
frameworks,
countCreatedTopLevelSourceMappings,
countCreatedTransitiveSourceMappings);
if (packageToInstallWasTransitive.HasValue)
{
actionTelemetryEvent.PackageToInstallWasTransitive = packageToInstallWasTransitive.Value;
}
actionTelemetryEvent["InstalledPackageEnumerationTimeInMilliseconds"] = packageEnumerationTime.ElapsedMilliseconds;
_telemetryProvider.EmitEvent(actionTelemetryEvent);
}
}, cancellationToken);
}