/// <summary> /// Add a hyperlink to the /// </summary> /// <param name="text">Display text of the hyperlink</param> /// <param name="uri">Uri of the hyperlink</param> /// <param name="parent">Parent node in the build log</param> /// <returns>The new external link containing the supplied hyperlink</returns> protected static IExternalLink AddLinkNode(string text, Uri uri, IBuildInformationNode parent) { var link = parent.Children.AddExternalLink(text, uri); link.Save(); return(link); }
/// <summary> /// Analyzes the resulting metrics file and compares the Maintainability Index and Cyclomatic Complexity against the threshold values /// </summary> /// <param name="member">Name of the member (namespace, module, type...)</param> /// <param name="metrics">The metrics for this member</param> /// <param name="parent">The parent node in the build log</param> private void ProcessMetrics(string member, IEnumerable <Metric> metrics, IBuildInformationNode parent) { foreach (var metric in metrics) { int metricValue; if (metric != null && !string.IsNullOrEmpty(metric.Value) && int.TryParse(metric.Value, out metricValue)) { if (metric.Name == MaintainabilityIndex && Convert.ToInt32(metric.Value) < this.MaintainabilityIndexErrorThreshold.Get(this.ActivityContext)) { this.FailCurrentBuild(); LogBuildError(string.Format("{0} for {1} is {2} which is below threshold ({3})", MaintainabilityIndex, member, metric.Value, this.MaintainabilityIndexErrorThreshold.Get(this.ActivityContext))); } if (metric.Name == MaintainabilityIndex && metricValue < this.MaintainabilityIndexWarningThreshold.Get(this.ActivityContext)) { this.PartiallyFailCurrentBuild(); LogBuildError(string.Format("{0} for {1} is {2} which is below threshold ({3})", MaintainabilityIndex, member, metric.Value, this.MaintainabilityIndexWarningThreshold.Get(this.ActivityContext))); } if (metric.Name == CyclomaticComplexity && metricValue > this.CyclomaticComplexityErrorThreshold.Get(this.ActivityContext)) { this.FailCurrentBuild(); this.LogBuildError(string.Format("{0} for {1} is {2} which is above threshold ({3})", CyclomaticComplexity, member, metric.Value, this.CyclomaticComplexityErrorThreshold.Get(this.ActivityContext))); } if (metric.Name == CyclomaticComplexity && metricValue > this.CyclomaticComplexityWarningThreshold.Get(this.ActivityContext)) { this.PartiallyFailCurrentBuild(); this.LogBuildError(string.Format("{0} for {1} is {2} which is above threshold ({3})", CyclomaticComplexity, member, metric.Value, this.CyclomaticComplexityWarningThreshold.Get(this.ActivityContext))); } AddTextNode(metric.Name + ": " + metric.Value, parent); } } }
/// <summary> /// Add a text node to the build log /// </summary> /// <param name="text">Display text</param> /// <param name="parent">Parent node in the build log</param> /// <returns>The new node containing the supplied text</returns> protected static IBuildInformationNode AddTextNode(string text, IBuildInformationNode parent) { IBuildInformationNode childNode = parent.Children.CreateNode(); childNode.Type = parent.Type; childNode.Fields.Add("DisplayText", text); childNode.Save(); return(childNode); }
/// <summary> /// Executes the logic for this workflow activity /// </summary> protected override void InternalExecute() { this.BuildDetail = this.ActivityContext.GetExtension <IBuildDetail>(); string generatedFile = "Metrics.xml"; if (this.GeneratedFileName != null && !string.IsNullOrEmpty(this.GeneratedFileName.Get(this.ActivityContext))) { generatedFile = Path.Combine(this.BuildDetail.DropLocation, this.GeneratedFileName.Get(this.ActivityContext)); } if (!this.RunCodeMetrics(generatedFile)) { return; } IActivityTracking currentTracking = this.ActivityContext.GetExtension <IBuildLoggingExtension>().GetActivityTracking(this.ActivityContext); IBuildInformationNode rootNode = AddTextNode("Processing metrics", currentTracking.Node); string fileName = Path.GetFileName(generatedFile); string pathToFileInDropFolder = Path.Combine(this.BuildDetail.DropLocation, fileName); AddLinkNode(fileName, new Uri(pathToFileInDropFolder), rootNode); CodeMetricsReport result = CodeMetricsReport.LoadFromFile(generatedFile); if (result == null) { LogBuildMessage("Could not load metric result file "); return; } foreach (var target in result.Targets) { var targetNode = AddTextNode("Target: " + target.Name, rootNode); foreach (var module in target.Modules) { var moduleNode = AddTextNode("Module: " + module.Name, targetNode); this.ProcessMetrics(module.Name, module.Metrics, moduleNode); foreach (var ns in module.Namespaces) { var namespaceNode = AddTextNode("Namespace: " + ns.Name, moduleNode); this.ProcessMetrics(ns.Name, ns.Metrics, namespaceNode); foreach (var type in ns.Types) { var typeNode = AddTextNode("Type: " + type.Name, namespaceNode); this.ProcessMetrics(type.Name, type.Metrics, typeNode); foreach (var member in type.Members) { var memberNode = AddTextNode("Member: " + member.Name, typeNode); this.ProcessMetrics(member.Name, member.Metrics, memberNode); } } } } } }
private void RecurseStepsNode(IBuildInformationNode node, List <IBuildInformationNode> result, int level) { foreach (IBuildInformationNode childNode in node.Children.Nodes) { string status = childNode.Fields["Status"]; bool successed = status.ToLower() == "succeeded"; //if (!successed || (level <= 1)) { result.Add(childNode); RecurseStepsNode(childNode, result, level + 1); } } }
private void ChangeBuildAssigment(string teamProject, WorkItem workItem, string buildNumber, bool removeAction) { IBuildDetail buildDetail = Context.GetBuild(teamProject, buildNumber); if (buildDetail == null) { return; } try { List <IBuildInformationNode> nodes = buildDetail.Information.GetNodesByType("AssociatedWorkItem"); IBuildInformationNode associatedWI_Node = nodes.Find(node => { return(node.Fields["WorkItemUri"] == workItem.Uri.ToString()); }); if (removeAction) { if (associatedWI_Node != null) { associatedWI_Node.Delete(); } } else { if (associatedWI_Node == null) { IBuildInformationNode node = buildDetail.Information.CreateNode(); node.Type = "AssociatedWorkItem"; node.Fields[CoreField.Title.ToString()] = workItem.Title; node.Fields[CoreField.AssignedTo.ToString()] = workItem.Fields[CoreField.AssignedTo].Value as string; node.Fields["WorkItemUri"] = workItem.Uri.ToString(); node.Fields["Status"] = workItem.State; node.Fields["WorkItemId"] = workItem.Id.ToString(); node.Save(); } } } catch (Exception e) { MessageBox.Show( string.Format("Changing build assigment for workitem id: {0} ('{1}') failed, message: {2}", workItem.Id, workItem.Title, e.Message), "Change build assigment", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
/// <summary> /// Analyzes the resulting metrics file and compares the metrics against the threshold values /// </summary> /// <param name="parent">The parent node in the build log</param> /// <param name="metrics">List of metric values</param> /// <param name="thresholds">Thresholds for errors and warnings </param> /// <param name="fullyQualifiedName">FQN for the method/type under test</param> /// <param name="currentName">Name of current method/type </param> /// <returns>Number if violations</returns> private int ProcessMetrics(IBuildInformationNode parent, IEnumerable <Metric> metrics, SpecificMetricThresholds thresholds, string fullyQualifiedName, string currentName) { var thecomplainlist = new List <string>(); foreach (var metric in metrics.Where(metric => metric != null && !string.IsNullOrEmpty(metric.Value))) { switch (metric.Name) { case MaintainabilityIndex: this.CheckLimits(metric, thresholds.MIMetricChecker, fullyQualifiedName, thecomplainlist); break; case CyclomaticComplexity: this.CheckLimits(metric, thresholds.CCMetricChecker, fullyQualifiedName, thecomplainlist); break; case ClassCoupling: this.CheckLimits(metric, thresholds.COMetricChecker, fullyQualifiedName, thecomplainlist); break; case DepthOfInheritance: this.CheckLimits(metric, thresholds.DOIMetricChecker, fullyQualifiedName, thecomplainlist); break; case LinesOfCode: this.CheckLimits(metric, thresholds.LOCMetricChecker, fullyQualifiedName, thecomplainlist); break; default: throw new UnknownMetricNameException(metric.Name); } } if (this.logCodeMetrics && parent != null && thecomplainlist.Count > 0) { var result = "Metrics for " + currentName + @"\n"; thecomplainlist.ForEach(s => result += s + @"\n"); BaseCodeActivity.AddTextNode(result, parent); } return(thecomplainlist.Count); }
static string ShowChild(IBuildInformationNode node, int level) { string levelStr = new string(' ', level * 4); foreach (var field in node.Fields) { if (field.Key == "ReservedAgentName") { return field.Value; } } foreach (var child in node.Children.Nodes) { string AgentName = ShowChild(child, level + 1); if (!string.IsNullOrEmpty(AgentName)) { return AgentName; } } return string.Empty; }
internal BuildDetailSectionStatus Initialize(IBuildDetail bd) { var result = new BuildDetailSectionStatus(); bd.RefreshAllDetails(); var workItemsOpened = new StringBuilder(); List <IBuildInformationNode> workItemsOpenedNodes = bd.Information.GetNodesByType("OpenedWorkItem"); workItemsOpenedNodes.ForEach(node => { workItemsOpened.AppendFormat("{0} ({1}/{2}) ; ", node.Fields["WorkItemId"], node.Fields["Status"], node.Fields["AssignedTo"]); }); List <IBuildInformationNode> steps = bd.Information.GetNodesByType("BuildStep"); IBuildInformationNode buildCompletedNode = steps.Find(node => { return(node.Fields["Name"] == "BuildCompleted"); }); if (buildCompletedNode != null) { result.Success = (buildCompletedNode.Fields["Status"].ToLower() == "succeeded"); result.Message = buildCompletedNode.Fields["Message"]; } else { result.Success = false; result.Message = string.Empty; } // build name link label var control_BuildName = new LinkLabel(); control_BuildName.BackColor = Color.Transparent; control_BuildName.Text = bd.BuildNumber; control_BuildName.AutoSize = true; control_BuildName.Tag = bd.DropLocation; control_BuildName.Click += BuildName_Click; // log file link label var control_Log = new LinkLabel(); control_Log.BackColor = Color.Transparent; control_Log.Text = bd.LogLocation; control_Log.AutoSize = true; control_Log.Click += LogFile_Click; var itemValues = new List <object> { control_BuildName, bd.RequestedBy, bd.BuildDefinition.TeamProject, bd.BuildDefinition.Name, string.Empty, //bd.BuildAgent.Name, string.Empty, //bd.CommandLineArguments, bd.StartTime.ToString(), bd.FinishTime.ToString(), bd.LastChangedBy, bd.LastChangedOn.ToString(), bd.Quality, workItemsOpened.ToString(), bd.SourceGetVersion, control_Log }; this.lvItems.BeginUpdate(); try { this.lvItems.Controls.Clear(); this.table.Rows.Clear(); for (int i = 0; i < this.itemNames.Length; i++) { string itemName = this.itemNames[i]; var itemValue = (itemValues[i] is string?itemValues[i] : string.Empty) as string; var newRow = new Row(); newRow.Cells.Add(new Cell(itemName)); var cell = new Cell(itemValue); newRow.Cells.Add(cell); this.table.Rows.Add(newRow); if (itemValues[i] is Control) { Rectangle cellRect = this.lvItems.CellRect(cell); var control = itemValues[i] as Control; this.lvItems.Controls.Add(control); control.Location = new Point(cellRect.X, cellRect.Y - 1); control.BringToFront(); } } } finally { this.lvItems.EndUpdate(); Rectangle rectangle = this.lvItems.ColumnRect(1); this.ValueColumnLeftPos = rectangle.Left + 14; } #region debug // Debug.WriteLine(new string('-', 120)); // foreach (var node in bd.Information.Nodes) // { // Debug.WriteLine(string.Format("Type: {0}", node.Type)); // foreach (var field in node.Fields) // { // Debug.WriteLine(string.Format(" Field: {0} | Value: {1}", field.Key, field.Value)); // } // Debug.WriteLine(new string('=', 120)); // } #endregion return(result); }
/// <summary> /// Executes the logic for this workflow activity /// </summary> protected override void InternalExecute() { this.logCodeMetrics = this.LogCodeMetrics.Get(this.ActivityContext); this.BuildDetail = this.ActivityContext.GetExtension <IBuildDetail>(); string generatedFile = "Metrics.xml"; string outputLocation = this.BuildDetail.DropLocation; if (string.IsNullOrEmpty(outputLocation)) { outputLocation = this.BinariesDirectory.Get(this.ActivityContext); } if (this.GeneratedFileName != null && !string.IsNullOrEmpty(this.GeneratedFileName.Get(this.ActivityContext))) { generatedFile = Path.Combine(outputLocation, this.GeneratedFileName.Get(this.ActivityContext)); } if (!this.RunCodeMetrics(generatedFile)) { return; } IActivityTracking currentTracking = this.ActivityContext.GetExtension <IBuildLoggingExtension>().GetActivityTracking(this.ActivityContext); if (!this.AnalyzeMetricsResult.Get(this.ActivityContext)) { BaseCodeActivity.AddTextNode("Skipped code metrics analysis (not set to run)", currentTracking.Node); return; } IBuildInformationNode rootNode = AddTextNode("Analyzing code metrics results", currentTracking.Node); string fileName = Path.GetFileName(generatedFile); string pathToFileInDropFolder = Path.Combine(outputLocation, fileName); BaseCodeActivity.AddLinkNode(fileName, new Uri(pathToFileInDropFolder), rootNode); CodeMetricsReport result = CodeMetricsReport.LoadFromFile(generatedFile); if (result == null) { this.LogBuildMessage("Could not load metric result file "); return; } // Get thresholds for each level. var memberMetricThresholds = new MethodMetricsThresholds(this); var typeMetricThresholds = new TypeMetricsThresholds(this, memberMetricThresholds); // var namespaceMetricThresholds = new NameSpaceMetricsThresholds(this); //Uncomment in this if you want to perform metric checks on namespaces // var assemblyMetricThresholds = new AssemblyMetricsThresholds(this); // Uncomment in this if you want to perform metric checks on assemblies int noOfTypeViolations = 0; int noOfMethodViolations = 0; foreach (var target in result.Targets) { var targetNode = this.logCodeMetrics ? AddTextNode("Target: " + target.Name, rootNode) : null; foreach (var module in target.Modules) { var moduleNode = this.logCodeMetrics ? AddTextNode("Module: " + module.Name, targetNode) : null; foreach (var ns in module.Namespaces) { var namespaceNode = this.logCodeMetrics ? AddTextNode("Namespace: " + ns.Name, moduleNode) : null; foreach (var type in ns.Types) { var typeNode = this.logCodeMetrics ? AddTextNode("Type: " + type.Name, namespaceNode) : null; var typeInformation = new MemberInformation(null, type, ns, module); noOfTypeViolations += this.ProcessMetrics(typeNode, typeInformation.TheClass.Metrics, typeMetricThresholds, typeInformation.FullyQualifiedName, typeInformation.TheClass.Name); noOfMethodViolations += (from member in type.Members let memberNode = this.logCodeMetrics ? AddTextNode("Member: " + member.Name + " " + member.MetricsInformation, typeNode) : null let memberInformation = new MemberInformation(member, type, ns, module) select this.ProcessMetrics(memberNode, memberInformation.TheMember.Metrics, memberMetricThresholds, memberInformation.FullyQualifiedName, memberInformation.TheMember.Name)).Sum(); } } } } var numberMessageTypes = string.Format("Number of Code Metric warnings/errors on types: {0}", noOfTypeViolations); var numberMessageMethods = string.Format("Number of Code Metric warnings/errors on methods: {0}", noOfMethodViolations); BaseCodeActivity.AddTextNode(numberMessageTypes, rootNode); BaseCodeActivity.AddTextNode(numberMessageMethods, rootNode); }
/// <summary> /// Add a hyperlink to the /// </summary> /// <param name="text">Display text of the hyperlink</param> /// <param name="uri">Uri of the hyperlink</param> /// <param name="parent">Parent node in the build log</param> /// <returns>The new external link containing the supplied hyperlink</returns> protected static IExternalLink AddLinkNode(string text, Uri uri, IBuildInformationNode parent) { var link = parent.Children.AddExternalLink(text, uri); link.Save(); return link; }
/// <summary> /// Add a text node to the build log /// </summary> /// <param name="text">Display text</param> /// <param name="parent">Parent node in the build log</param> /// <returns>The new node containing the supplied text</returns> protected static IBuildInformationNode AddTextNode(string text, IBuildInformationNode parent) { IBuildInformationNode childNode = parent.Children.CreateNode(); childNode.Type = parent.Type; childNode.Fields.Add("DisplayText", text); childNode.Save(); return childNode; }
/// <summary> /// Analyzes the resulting metrics file and compares the metrics against the threshold values /// </summary> /// <param name="parent">The parent node in the build log</param> /// <param name="metrics">List of metric values</param> /// <param name="thresholds">Thresholds for errors and warnings </param> /// <param name="fullyQualifiedName">FQN for the method/type under test</param> /// <param name="currentName">Name of current method/type </param> /// <returns>Number if violations</returns> private int ProcessMetrics(IBuildInformationNode parent, IEnumerable<Metric> metrics, SpecificMetricThresholds thresholds, string fullyQualifiedName, string currentName) { var thecomplainlist = new List<string>(); foreach (var metric in metrics.Where(metric => metric != null && !string.IsNullOrEmpty(metric.Value))) { switch (metric.Name) { case MaintainabilityIndex: this.CheckLimits(metric, thresholds.MIMetricChecker, fullyQualifiedName, thecomplainlist); break; case CyclomaticComplexity: this.CheckLimits(metric, thresholds.CCMetricChecker, fullyQualifiedName, thecomplainlist); break; case ClassCoupling: this.CheckLimits(metric, thresholds.COMetricChecker, fullyQualifiedName, thecomplainlist); break; case DepthOfInheritance: this.CheckLimits(metric, thresholds.DOIMetricChecker, fullyQualifiedName, thecomplainlist); break; case LinesOfCode: this.CheckLimits(metric, thresholds.LOCMetricChecker, fullyQualifiedName, thecomplainlist); break; default: throw new UnknownMetricNameException(metric.Name); } } if (this.logCodeMetrics && parent != null && thecomplainlist.Count > 0) { var result = "Metrics for " + currentName + @"\n"; thecomplainlist.ForEach(s => result += s + @"\n"); BaseCodeActivity.AddTextNode(result, parent); } return thecomplainlist.Count; }