/// <summary>
        ///     Log the graph in graphviz (dot) format.
        /// </summary>
        /// <param name="graph">Graph to log</param>
        /// <remarks>
        /// Example of a graphviz graph description
        ///  
        /// digraph graphname {
        ///    a -> b -> c;
        ///    b -> d;
        /// }
        ///  
        /// For more info see https://www.graphviz.org/
        /// </remarks>
        /// <returns>Async task</returns>
        private async Task LogGraphViz(Channel targetChannel, DependencyFlowGraph graph)
        {
            StringBuilder subgraphClusterWriter = null;
            bool writeToSubgraphCluster = targetChannel != null;
            if (writeToSubgraphCluster)
            {
                subgraphClusterWriter = new StringBuilder();
                subgraphClusterWriter.AppendLine($"    subgraph cluster_{OutputHelpers.CalculateGraphVizNodeName(targetChannel.Name)} {{");
                subgraphClusterWriter.AppendLine($"        label = \"{targetChannel.Name}\"");
            }

            using (StreamWriter writer = OutputHelpers.GetOutputFileStreamOrConsole(_options.GraphVizOutputFile))
            {
                await writer.WriteLineAsync("digraph repositoryGraph {");
                await writer.WriteLineAsync("    node [shape=record]");
                foreach (DependencyFlowNode node in graph.Nodes)
                {
                    StringBuilder nodeBuilder = new StringBuilder();

                    // First add the node name
                    nodeBuilder.Append($"    {OutputHelpers.CalculateGraphVizNodeName(node)}");

                    // Then add the label.  label looks like [label="<info here>"]
                    nodeBuilder.Append("[label=\"");

                    // Append friendly repo name
                    nodeBuilder.Append(OutputHelpers.GetSimpleRepoName(node.Repository));
                    nodeBuilder.Append(@"\n");

                    // Append branch name
                    nodeBuilder.Append(node.Branch);

                    // Append end of label and end of node.
                    nodeBuilder.Append("\"];");

                    // If highlighting a specific channel, Add those nodes to a subgraph cluster
                    // if they output to the subgraph cluster.
                    if (writeToSubgraphCluster && node.OutputChannels.Any(c => c == targetChannel.Name))
                    {
                        subgraphClusterWriter.AppendLine($"        {OutputHelpers.CalculateGraphVizNodeName(node)}");
                    }

                    // Write it out.
                    await writer.WriteLineAsync(nodeBuilder.ToString());
                }

                // Now write all the edges
                foreach (DependencyFlowEdge edge in graph.Edges)
                {
                    string fromNode = OutputHelpers.CalculateGraphVizNodeName(edge.From);
                    string toNode = OutputHelpers.CalculateGraphVizNodeName(edge.To);
                    string label = $"{edge.Subscription.Channel.Name} ({edge.Subscription.Policy.UpdateFrequency})";

                    if (writeToSubgraphCluster && edge.Subscription.Channel.Name == targetChannel.Name)
                    {
                        subgraphClusterWriter.AppendLine($"    {fromNode} -> {toNode} [{GetEdgeStyle(edge)}]");
                    }
                    else
                    {
                        await writer.WriteLineAsync($"    {fromNode} -> {toNode} [{GetEdgeStyle(edge)}]");
                    }
                }

                if (writeToSubgraphCluster)
                {
                    await writer.WriteLineAsync(subgraphClusterWriter.ToString());
                    await writer.WriteLineAsync("    }");
                }

                // Write a legend

                await writer.WriteLineAsync("    subgraph cluster1 {");
                await writer.WriteLineAsync("        rankdir=RL;");
                await writer.WriteLineAsync("        label = \"Legend\"");
                await writer.WriteLineAsync("        shape = rectangle;");
                await writer.WriteLineAsync("        color = black;");
                await writer.WriteLineAsync("        a[style = invis];");
                await writer.WriteLineAsync("        b[style = invis];");
                await writer.WriteLineAsync("        c[style = invis];");
                await writer.WriteLineAsync("        d[style = invis];");
                await writer.WriteLineAsync("        e[style = invis];");
                await writer.WriteLineAsync("        f[style = invis];");
                await writer.WriteLineAsync("        c->d[label = \"Updated Every Build\", style = bold];");
                await writer.WriteLineAsync("        a->b[label = \"Updated Every Day\", style = dashed];");
                await writer.WriteLineAsync("        e->f[label = \"Disabled/Updated On-demand\", style = dotted];");
                await writer.WriteLineAsync("    }");
            
                await writer.WriteLineAsync("}");
            }
        }
Пример #2
0
        /// <summary>
        ///     Log the graph in graphviz (dot) format.
        /// </summary>
        /// <param name="graph">Graph to log</param>
        /// <remarks>
        /// Example of a graphviz graph description
        ///
        /// digraph graphname {
        ///    a -> b -> c;
        ///    b -> d;
        /// }
        ///
        /// For more info see https://www.graphviz.org/
        /// </remarks>
        /// <returns>Async task</returns>
        private async Task LogGraphViz(DependencyGraph graph)
        {
            using (StreamWriter writer = OutputHelpers.GetOutputFileStreamOrConsole(_options.GraphVizOutputFile))
            {
                await writer.WriteLineAsync("digraph repositoryGraph {");

                await writer.WriteLineAsync("    node [shape=record]");

                foreach (DependencyGraphNode node in graph.Nodes)
                {
                    StringBuilder nodeBuilder = new StringBuilder();

                    // First add the node name
                    nodeBuilder.Append($"    {OutputHelpers.CalculateGraphVizNodeName(node)}");

                    // Then add the label.  label looks like [label="<info here>"]
                    nodeBuilder.Append("[label=\"");

                    // Append friendly repo name
                    nodeBuilder.Append(OutputHelpers.GetSimpleRepoName(node.Repository));
                    nodeBuilder.Append(@"\n");

                    // Append short commit sha
                    nodeBuilder.Append(node.Commit.Substring(0, node.Commit.Length < 10 ? node.Commit.Length : 10));

                    // Append a build string (with newline) if available
                    if (node.ContributingBuilds != null && node.ContributingBuilds.Any())
                    {
                        Build newestBuild = node.ContributingBuilds.OrderByDescending(b => b.DateProduced).First();
                        nodeBuilder.Append($"\\n{newestBuild.DateProduced.ToString("g")} (UTC)");
                    }

                    // Append a diff string if the graph contains diff info.
                    GitDiff diffFrom = node.DiffFrom;
                    if (diffFrom != null)
                    {
                        if (!diffFrom.Valid)
                        {
                            nodeBuilder.Append("\\ndiff unknown");
                        }
                        else if (diffFrom.Ahead != 0 || diffFrom.Behind != 0)
                        {
                            if (node.DiffFrom.Ahead != 0)
                            {
                                nodeBuilder.Append($"\\nahead: {node.DiffFrom.Ahead} commits");
                            }
                            if (node.DiffFrom.Behind != 0)
                            {
                                nodeBuilder.Append($"\\nbehind: {node.DiffFrom.Behind} commits");
                            }
                        }
                        else
                        {
                            nodeBuilder.Append("\\nlatest");
                        }
                    }

                    // Append end of label and end of node.
                    nodeBuilder.Append("\"];");

                    // Write it out.
                    await writer.WriteLineAsync(nodeBuilder.ToString());

                    // Now write the edges
                    foreach (DependencyGraphNode childNode in node.Children)
                    {
                        await writer.WriteLineAsync($"    {OutputHelpers.CalculateGraphVizNodeName(node)} -> {OutputHelpers.CalculateGraphVizNodeName(childNode)}");
                    }
                }

                await writer.WriteLineAsync("}");
            }
        }