/// <summary> /// Execute the installs/uninstalls /// </summary> protected async Task ExecuteActionsAsync(IEnumerable <ResolvedAction> actions, NuGetUIProjectContext projectContext, UserAction userAction, CancellationToken token) { var processedDirectInstalls = new HashSet <PackageIdentity>(PackageIdentity.Comparer); foreach (var projectActions in actions.GroupBy(e => e.Project)) { var nuGetProjectActions = projectActions.Select(e => e.Action); var directInstall = GetDirectInstall(nuGetProjectActions, userAction, projectContext.CommonOperations); if (directInstall != null && !processedDirectInstalls.Contains(directInstall)) { NuGetPackageManager.SetDirectInstall(directInstall, projectContext); processedDirectInstalls.Add(directInstall); } await _packageManager.ExecuteNuGetProjectActionsAsync(projectActions.Key, nuGetProjectActions, projectContext, token); NuGetPackageManager.ClearDirectInstall(projectContext); } }
public override IEnumerable <IProjectContextInfo> GetSelectedProjects(UserAction action) { return(_nugetProjects); }
private async Task PerformActionImplAsync( IServiceBroker serviceBroker, INuGetProjectManagerService projectManagerService, INuGetUI uiService, ResolveActionsAsync resolveActionsAsync, NuGetOperationType 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> >(); List <Tuple <string, string> > addedPackages = null; List <Tuple <string, string> > updatedPackagesOld = null; List <Tuple <string, string> > updatedPackagesNew = null; // Enable granular level telemetry events for nuget ui operation uiService.ProjectContext.OperationId = Guid.NewGuid(); Stopwatch packageEnumerationTime = new Stopwatch(); packageEnumerationTime.Start(); try { // collect the install state of the existing packages foreach (IProjectContextInfo project in uiService.Projects) { IEnumerable <IPackageReferenceContextInfo> installedPackages = await project.GetInstalledPackagesAsync( uiService.UIContext.ServiceBroker, cancellationToken); foreach (IPackageReferenceContextInfo package in installedPackages) { Tuple <string, string> packageInfo = new Tuple <string, string>( package.Identity.Id, (package.Identity.Version == null ? "" : package.Identity.Version.ToNormalizedString())); if (!existingPackages.Contains(packageInfo)) { existingPackages.Add(packageInfo); } } } } catch (Exception) { // don't teardown the process if we have a telemetry failure } packageEnumerationTime.Stop(); await _lockService.ExecuteNuGetOperationAsync(async() => { try { uiService.BeginOperation(); using (INuGetProjectUpgraderService projectUpgrader = await serviceBroker.GetProxyAsync <INuGetProjectUpgraderService>( NuGetServices.ProjectUpgraderService, cancellationToken)) { bool isAcceptedFormat = await CheckPackageManagementFormatAsync(projectUpgrader, uiService, cancellationToken); if (!isAcceptedFormat) { return; } } TelemetryServiceUtility.StartOrResumeTimer(); IReadOnlyList <ProjectAction> actions = await resolveActionsAsync(projectManagerService); IReadOnlyList <PreviewResult> results = await GetPreviewResultsAsync(projectManagerService, actions, cancellationToken); if (operationType == NuGetOperationType.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 = NuGetOperationType.Update; } } 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) { 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; } } PackageLoadContext plc = new PackageLoadContext(sourceRepositories: null, isSolution: false, uiService.UIContext); var frameworks = (await plc.GetSupportedFrameworksAsync()).ToList(); string[] projectIds = (await ProjectUtility.GetSortedProjectIdsAsync( uiService.UIContext.ServiceBroker, uiService.Projects, cancellationToken)).ToArray(); var actionTelemetryEvent = new VSActionsTelemetryEvent( uiService.ProjectContext.OperationId.ToString(), projectIds, operationType, OperationSource.UI, startTime, status, packageCount, DateTimeOffset.Now, duration.TotalSeconds); var nuGetUI = uiService as NuGetUI; AddUiActionEngineTelemetryProperties( actionTelemetryEvent, continueAfterPreview, acceptedLicense, userAction, nuGetUI?.SelectedIndex, nuGetUI?.RecommendedCount, nuGetUI?.RecommendPackages, nuGetUI?.RecommenderVersion, existingPackages, addedPackages, removedPackages, updatedPackagesOld, updatedPackagesNew, frameworks); actionTelemetryEvent["InstalledPackageEnumerationTimeInMilliseconds"] = packageEnumerationTime.ElapsedMilliseconds; TelemetryActivity.EmitTelemetryEvent(actionTelemetryEvent); } }, cancellationToken); }
/// <summary> /// Returns the list of projects that are selected for the given action /// </summary> public abstract IEnumerable <NuGetProject> GetSelectedProjects(UserAction action);
private static void AddUiActionEngineTelemetryProperties( VSActionsTelemetryEvent actionTelemetryEvent, bool continueAfterPreview, bool acceptedLicense, UserAction userAction, List <Tuple <string, string> > addedPackages, List <string> removedPackages, List <Tuple <string, string> > updatedPackagesOld, List <Tuple <string, string> > updatedPackagesNew) { TelemetryEvent ToTelemetryPackage(Tuple <string, string> package) { var subEvent = new TelemetryEvent(eventName: null); subEvent.AddPiiData("id", package.Item1); subEvent["version"] = package.Item2; return(subEvent); } List <TelemetryEvent> ToTelemetryPackageList(List <Tuple <string, string> > packages) { var list = new List <TelemetryEvent>(packages.Count); list.AddRange(packages.Select(ToTelemetryPackage)); return(list); } // log possible cancel reasons if (!continueAfterPreview) { actionTelemetryEvent["CancelAfterPreview"] = "True"; } if (!acceptedLicense) { actionTelemetryEvent["AcceptedLicense"] = "False"; } // log the single top level package the user is installing or removing if (userAction != null) { // userAction.Version can be null for deleted packages. actionTelemetryEvent.ComplexData["SelectedPackage"] = ToTelemetryPackage(new Tuple <string, string>(userAction.PackageId, userAction.Version?.ToNormalizedString() ?? string.Empty)); } // other packages can be added, removed, or upgraded as part of bulk upgrade or as part of satisfying package dependencies, so log that also if (addedPackages != null && addedPackages.Count > 0) { var packages = new List <TelemetryEvent>(); foreach (var package in addedPackages) { packages.Add(ToTelemetryPackage(package)); } actionTelemetryEvent.ComplexData["AddedPackages"] = packages; } if (removedPackages != null && removedPackages.Count > 0) { var packages = new List <TelemetryPiiProperty>(); foreach (var package in removedPackages) { packages.Add(new TelemetryPiiProperty(package)); } actionTelemetryEvent.ComplexData["RemovedPackages"] = packages; } // two collections for updated packages: pre and post upgrade if (updatedPackagesNew != null && updatedPackagesNew.Count > 0) { actionTelemetryEvent.ComplexData["UpdatedPackagesNew"] = ToTelemetryPackageList(updatedPackagesNew); } if (updatedPackagesOld != null && updatedPackagesOld.Count > 0) { actionTelemetryEvent.ComplexData["UpdatedPackagesOld"] = ToTelemetryPackageList(updatedPackagesOld); } }
public override IEnumerable <NuGetProject> GetSelectedProjects(UserAction action) { return(_nugetProjects); }
/// <summary> /// The internal implementation to perform user action. /// </summary> /// <param name="resolveActionsAsync">A function that returns a task that resolves the user /// action into project actions.</param> /// <param name="executeActionsAsync">A function that returns a task that executes /// the project actions.</param> private async Task PerformActionImplAsync( INuGetUI uiService, Func <SourceCacheContext, Task <IReadOnlyList <ResolvedAction> > > resolveActionsAsync, Func <IReadOnlyList <ResolvedAction>, SourceCacheContext, Task> executeActionsAsync, NuGetOperationType operationType, UserAction userAction, CancellationToken token) { var status = NuGetOperationStatus.Succeeded; var startTime = DateTimeOffset.Now; var packageCount = 0; bool continueAfterPreview = true; bool acceptedLicense = true; List <string> removedPackages = null; List <Tuple <string, string> > addedPackages = null; List <Tuple <string, string> > updatedPackagesOld = null; List <Tuple <string, string> > updatedPackagesNew = null; // Enable granular level telemetry events for nuget ui operation uiService.ProjectContext.OperationId = Guid.NewGuid(); await _lockService.ExecuteNuGetOperationAsync(async() => { try { uiService.BeginOperation(); var acceptedFormat = await CheckPackageManagementFormat(uiService, token); if (!acceptedFormat) { return; } TelemetryServiceUtility.StartOrResumeTimer(); using (var sourceCacheContext = new SourceCacheContext()) { var actions = await resolveActionsAsync(sourceCacheContext); var results = GetPreviewResults(actions); if (operationType == NuGetOperationType.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 = NuGetOperationType.Update; } } TelemetryServiceUtility.StopTimer(); // Show the preview window. if (uiService.DisplayPreviewWindow) { var shouldContinue = uiService.PromptForPreviewAcceptance(results); if (!shouldContinue) { continueAfterPreview = false; return; } } TelemetryServiceUtility.StartOrResumeTimer(); // Show the license acceptance window. var accepted = await CheckLicenseAcceptanceAsync(uiService, results, token); TelemetryServiceUtility.StartOrResumeTimer(); if (!accepted) { acceptedLicense = false; return; } // Warn about the fact that the "dotnet" TFM is deprecated. if (uiService.DisplayDeprecatedFrameworkWindow) { var shouldContinue = ShouldContinueDueToDotnetDeprecation(uiService, actions, token); TelemetryServiceUtility.StartOrResumeTimer(); if (!shouldContinue) { return; } } if (!token.IsCancellationRequested) { // execute the actions await executeActionsAsync(actions, sourceCacheContext); // fires ActionsExecuted event to update the UI uiService.OnActionsExecuted(actions); } 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 actionTelemetryEvent = VSTelemetryServiceUtility.GetActionTelemetryEvent( uiService.ProjectContext.OperationId.ToString(), uiService.Projects, operationType, OperationSource.UI, startTime, status, packageCount, duration.TotalSeconds); AddUiActionEngineTelemetryProperties(actionTelemetryEvent, continueAfterPreview, acceptedLicense, userAction, addedPackages, removedPackages, updatedPackagesOld, updatedPackagesNew); TelemetryActivity.EmitTelemetryEvent(actionTelemetryEvent); } }, token); }
/// <summary> /// Perform a user action. /// </summary> /// <remarks>This needs to be called from a background thread. It may hang on the UI thread.</remarks> public async Task PerformAction(INuGetUI uiService, UserAction userAction, DependencyObject windowOwner, CancellationToken token) { try { uiService.ShowProgressDialog(windowOwner); var projects = uiService.Projects; // TODO: should stable packages allow prerelease dependencies if include prerelease was checked? // Allow prerelease packages only if the target is prerelease bool includePrelease = userAction.PackageIdentity.Version.IsPrerelease || userAction.Action == NuGetProjectActionType.Uninstall; bool includeUnlisted = userAction.Action == NuGetProjectActionType.Uninstall; ResolutionContext resolutionContext = new ResolutionContext(uiService.DependencyBehavior, includePrelease, includeUnlisted); IEnumerable<Tuple<NuGetProject, NuGetProjectAction>> actions = await GetActions( uiService, projects, userAction, removeDependencies: uiService.RemoveDependencies, forceRemove: uiService.ForceRemove, resolutionContext: resolutionContext, projectContext: uiService.ProgressWindow, token: token); IEnumerable<PreviewResult> results = GetPreviewResults(actions); // preview window if (uiService.DisplayPreviewWindow) { var shouldContinue = false; shouldContinue = uiService.PromptForPreviewAcceptance(results); if (!shouldContinue) { return; } } bool accepted = await CheckLicenseAcceptance(uiService, results, token); if (!accepted) { return; } if (!token.IsCancellationRequested) { // execute the actions await ExecuteActions(actions, uiService.ProgressWindow, userAction, token); // update uiService.RefreshPackageStatus(); } } catch (Exception ex) { uiService.ShowError(ex); } finally { uiService.CloseProgressDialog(); } }
/// <summary> /// Return the resolve package actions /// </summary> protected async Task<IEnumerable<Tuple<NuGetProject, NuGetProjectAction>>> GetActions( INuGetUI uiService, IEnumerable<NuGetProject> targets, UserAction userAction, bool removeDependencies, bool forceRemove, ResolutionContext resolutionContext, INuGetProjectContext projectContext, CancellationToken token) { List<Tuple<NuGetProject, NuGetProjectAction>> results = new List<Tuple<NuGetProject, NuGetProjectAction>>(); Debug.Assert(userAction.PackageId != null, "Package id can never be null in a User action"); if (userAction.Action == NuGetProjectActionType.Install) { Debug.Assert(userAction.PackageIdentity != null, "Package identity cannot be null when installing a package"); foreach (var target in targets) { IEnumerable<NuGetProjectAction> actions; actions = await _packageManager.PreviewInstallPackageAsync(target, userAction.PackageIdentity, resolutionContext, projectContext, uiService.ActiveSource, null, token); results.AddRange(actions.Select(a => new Tuple<NuGetProject, NuGetProjectAction>(target, a))); } } else { UninstallationContext uninstallationContext = new UninstallationContext( removeDependencies: removeDependencies, forceRemove: forceRemove); foreach (var target in targets) { IEnumerable<NuGetProjectAction> actions; if (userAction.PackageIdentity != null) { actions = await _packageManager.PreviewUninstallPackageAsync(target, userAction.PackageIdentity, uninstallationContext, projectContext, token); } else { actions = await _packageManager.PreviewUninstallPackageAsync(target, userAction.PackageId, uninstallationContext, projectContext, token); } results.AddRange(actions.Select(a => new Tuple<NuGetProject, NuGetProjectAction>(target, a))); } } return results; }
private PackageIdentity GetDirectInstall(IEnumerable<NuGetProjectAction> nuGetProjectActions, UserAction userAction, ICommonOperations commonOperations) { if (commonOperations != null && userAction != null && userAction.Action == NuGetProjectActionType.Install && nuGetProjectActions.Any()) { PackageIdentity directInstall = null; if(userAction.PackageIdentity != null) { directInstall = userAction.PackageIdentity; } else { var identitiesWithSameId = nuGetProjectActions.Where(n => n.PackageIdentity.Id.Equals(userAction)).ToList(); if(identitiesWithSameId.Count == 1) { directInstall = identitiesWithSameId[0].PackageIdentity; } // If there multiple actions with the same package id as the user action, just ignore } if (directInstall != null) { return directInstall; } } return null; }
/// <summary> /// Execute the installs/uninstalls /// </summary> /// <param name="actions"></param> /// <param name="projectContext"></param> /// <param name="token"></param> /// <returns></returns> protected async Task ExecuteActions(IEnumerable<Tuple<NuGetProject, NuGetProjectAction>> actions, NuGetUIProjectContext projectContext, UserAction userAction, CancellationToken token) { HashSet<PackageIdentity> processedDirectInstalls = new HashSet<PackageIdentity>(PackageIdentity.Comparer); foreach (var projectActions in actions.GroupBy(e => e.Item1)) { var nuGetProjectActions = projectActions.Select(e => e.Item2); var directInstall = GetDirectInstall(nuGetProjectActions, userAction, projectContext.CommonOperations); if (directInstall != null && !processedDirectInstalls.Contains(directInstall)) { NuGetPackageManager.SetDirectInstall(directInstall, projectContext); processedDirectInstalls.Add(directInstall); } await _packageManager.ExecuteNuGetProjectActionsAsync(projectActions.Key, nuGetProjectActions, projectContext, token); NuGetPackageManager.ClearDirectInstall(projectContext); } }