Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cmd/cycles.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var maxCycleLength int
var cyclesTopN int
var cyclesVerbose bool
var cyclesSplitTestOnly bool
var cyclesSVGOutput bool

// cyclesFinder implements Johnson's algorithm for finding all elementary cycles
// in a directed graph. Time complexity: O((V+E)(C+1)) where C is the number of cycles.
Expand Down Expand Up @@ -129,6 +130,9 @@ var cyclesCmd = &cobra.Command{
}
}
}
if cyclesSVGOutput {
return fmt.Errorf("--svg is not supported for cycles")
}

if jsonOutputCycles {
outputObj := map[string]interface{}{}
Expand Down Expand Up @@ -481,6 +485,7 @@ func init() {
cyclesCmd.Flags().IntVar(&maxCycleLength, "max-length", 0, "Limit cycles to length <= N (0 = no limit)")
cyclesCmd.Flags().IntVarP(&cyclesTopN, "top", "n", 10, "Number of top participants to show in summary")
cyclesCmd.Flags().BoolVar(&cyclesSplitTestOnly, "split-test-only", false, "Split cycles into test-only and non-test sections (uses go mod why -m)")
cyclesCmd.Flags().BoolVarP(&cyclesSVGOutput, "svg", "s", false, "(unsupported) placeholder for svg output")
cyclesCmd.Flags().StringSliceVar(&excludeModules, "exclude-modules", []string{}, "Exclude module path patterns (repeatable, supports * wildcard)")
cyclesCmd.Flags().StringSliceVarP(&mainModules, "mainModules", "m", []string{}, "Enter modules whose dependencies should be considered direct dependencies; defaults to the first module encountered in `go mod graph` output")
cyclesCmd.Flags().BoolVarP(&cyclesVerbose, "verbose", "v", false, "Include raw cycles with summary output")
Expand Down
23 changes: 21 additions & 2 deletions cmd/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ limitations under the License.
package cmd

import (
"bytes"
"encoding/json"
"fmt"
"os"
"os/exec"
"sort"
"strings"
"text/tabwriter"
Expand All @@ -36,6 +38,7 @@ var graphTopMode string
var graphTopN int
var graphVerbose bool
var graphSplitTestOnly bool
var graphSVGOutput bool

type graphNode struct {
Module string `json:"module"`
Expand Down Expand Up @@ -68,8 +71,8 @@ var graphCmd = &cobra.Command{
- Direct edges (solid blue): from main module(s) to their direct dependencies
- Transitive edges (dashed gray): dependencies of dependencies`,
RunE: func(cmd *cobra.Command, args []string) error {
if graphDotOutput && graphJSONOutput {
return fmt.Errorf("--dot and --json are mutually exclusive")
if (graphDotOutput && graphJSONOutput) || (graphSVGOutput && graphJSONOutput) || (graphDotOutput && graphSVGOutput) {
return fmt.Errorf("--dot, --svg, and --json are mutually exclusive")
}
if graphTopMode != "" && graphDotOutput {
return fmt.Errorf("cannot use --top with --dot")
Expand Down Expand Up @@ -178,6 +181,9 @@ var graphCmd = &cobra.Command{
fmt.Print(fileContents)
return nil
}
if graphSVGOutput {
return outputGraphSVG(fileContents)
}
if graphVerbose {
fmt.Println("Main modules:")
printDeps(overview.MainModules)
Expand Down Expand Up @@ -307,6 +313,18 @@ func chainContains(chain Chain, dep string) bool {
return false
}

func outputGraphSVG(dot string) error {
cmd := exec.Command("dot", "-Tsvg")
cmd.Stdin = strings.NewReader(dot)
cmd.Stdout = os.Stdout
var stderr bytes.Buffer
cmd.Stderr = &stderr
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to render DOT as SVG via graphviz 'dot': %w: %s", err, strings.TrimSpace(stderr.String()))
}
return nil
}

func colorMainNode(mainNode string) string {
return fmt.Sprintf("MainNode [label=\"%s\", style=\"filled\" color=\"yellow\"]\n", mainNode)
}
Expand Down Expand Up @@ -468,6 +486,7 @@ func init() {
graphCmd.Flags().BoolVar(&showEdgeTypes, "show-edge-types", false, "Distinguish direct vs transitive edges with colors/styles")
graphCmd.Flags().BoolVar(&graphDotOutput, "dot", false, "Output DOT graph to stdout")
graphCmd.Flags().BoolVarP(&graphJSONOutput, "json", "j", false, "Output graph data in JSON format")
graphCmd.Flags().BoolVarP(&graphSVGOutput, "svg", "s", false, "Render DOT output as SVG (requires graphviz 'dot')")
graphCmd.Flags().StringVar(&graphTopMode, "top", "", "Show top modules by degree: in, out, or both")
graphCmd.Flags().IntVarP(&graphTopN, "n", "n", 10, "Number of modules to show with --top")
graphCmd.Flags().BoolVar(&graphSplitTestOnly, "split-test-only", false, "Split graph into test-only and non-test sections (uses go mod why -m)")
Expand Down
Loading