gomodgraph

command
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 5, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

gomodgraph

A short example that loads the output of go mod graph into gograph and answers a few questions about a module's requirements. It only uses gograph and the standard library.

go mod graph | go run github.com/hmdsefi/gograph/examples/gomodgraph@latest cycles

Each line of go mod graph is from to, meaning from requires to. The example adds an edge from -> to for each line, so the dependencies of a module are the vertices it can reach, and the modules that depend on it are the vertices that can reach it. That's the opposite of a build graph, where an edge A -> B means A has to happen before B. Lines for the go and toolchain versions are skipped.

Commands

A <module> is either a module path, which matches every version, or path@version. The outputs below come from testdata/modgraph.txt.

cycles prints the module versions that require each other, one group per line. It uses connectivity.Tarjan:

$ gomodgraph -f testdata/modgraph.txt cycles
example.com/codec@v0.5.0 example.com/util@v0.3.1

dependents <module> prints every module that requires a version of <module>, directly or indirectly. It uses dag.Ancestors: the modules that can reach a module are the ones that require it.

$ gomodgraph -f testdata/modgraph.txt dependents example.com/util@v0.2.0
example.com/app
example.com/log@v1.1.0

why <module> prints the shortest requirement path from the main module, in the format of go mod why -m:

$ gomodgraph -f testdata/modgraph.txt why example.com/codec
# example.com/codec
example.com/app
example.com/lib@v1.4.0
example.com/util@v0.3.1
example.com/codec@v0.5.0

versions [module] groups the module versions by path and prints each path that the graph has at more than one version, or only the path of <module>. go mod graph lists every version that some module requires, and minimal version selection builds the highest one, so that's the version marked with *. Each version lists the modules that require it directly, which shows the requirement that sets it:

$ gomodgraph -f testdata/modgraph.txt versions
example.com/util
  v0.2.0  required by example.com/log@v1.1.0
* v0.3.1  required by example.com/codec@v0.5.0, example.com/lib@v1.4.0

A main module builds from its own code. When a dependency requires a version of it, its path is printed with (main module), and none of those versions is marked.

diff <old> <new> compares two saved outputs of go mod graph and prints the module versions and requirements that were removed (-) or added (+):

$ gomodgraph diff testdata/modgraph.txt testdata/modgraph_new.txt
- module example.com/lib@v1.4.0
- module example.com/log@v1.1.0
- module example.com/util@v0.2.0
+ module example.com/lib@v1.5.0
+ module example.com/metrics@v0.1.0
- require example.com/app example.com/lib@v1.4.0
...

diff -selected <old> <new> compares the version of each module path that builds: - for a module that left the build, + for a new one, and ~ for a version that changed. It matches what go list -m all reports on the two commits:

$ gomodgraph diff -selected testdata/modgraph.txt testdata/modgraph_new.txt
- example.com/log v1.1.0
+ example.com/metrics v0.1.0
~ example.com/lib v1.4.0 -> v1.5.0

The plain diff lists - module example.com/util@v0.2.0 for the same change, while example.com/util builds at v0.3.1 before and after. On a large module the gap is wider: between Prometheus v3.14.0 and v3.15.0, diff prints 3,488 lines and diff -selected prints 171.

mermaid <module> draws the module, the modules it requires directly, and the modules that require it directly. encoding/mermaid writes the picture as a flowchart, so it can sit in a pull request or a doc. The command leaves out the rest of the graph, which is usually too large for a diagram.

$ gomodgraph -f testdata/modgraph.txt mermaid example.com/app
flowchart TD
    n0["example.com/app"]
    n1["example.com/lib@v1.4.0"]
    n2["example.com/log@v1.1.0"]
    n0 --> n1
    n0 --> n2
flowchart TD
    n0["example.com/app"]
    n1["example.com/lib@v1.4.0"]
    n2["example.com/log@v1.1.0"]
    n0 --> n1
    n0 --> n2

Comment on pull requests that change go.mod

This workflow runs go mod graph on the base and head commits of a pull request and posts the diff -selected output as a comment, so the comment lists the modules whose version in the build changed. Pull requests from forks get a read-only token, so the comment step only works for branches in the same repository.

name: Module graph

on:
  pull_request:
    paths: [go.mod, go.sum]

permissions:
  contents: read
  pull-requests: write

jobs:
  diff:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-go@v5
        with:
          go-version: stable
      - name: Compare module graphs
        env:
          BASE: ${{ github.event.pull_request.base.sha }}
          HEAD: ${{ github.event.pull_request.head.sha }}
        run: |
          git checkout -q "$BASE" && go mod graph > "$RUNNER_TEMP/old.txt"
          git checkout -q "$HEAD" && go mod graph > "$RUNNER_TEMP/new.txt"
          {
            echo '```diff'
            go run github.com/hmdsefi/gograph/examples/gomodgraph@latest \
              diff -selected "$RUNNER_TEMP/old.txt" "$RUNNER_TEMP/new.txt"
            echo '```'
          } > "$RUNNER_TEMP/comment.md"
      - name: Comment
        env:
          GH_TOKEN: ${{ github.token }}
          PR: ${{ github.event.pull_request.number }}
        run: gh pr comment "$PR" --body-file "$RUNNER_TEMP/comment.md"

Documentation

Overview

Command gomodgraph loads the output of "go mod graph" into a gograph graph and answers a few questions about the module requirements:

go mod graph | go run github.com/hmdsefi/gograph/examples/gomodgraph cycles
go mod graph | go run github.com/hmdsefi/gograph/examples/gomodgraph versions
go mod graph | go run github.com/hmdsefi/gograph/examples/gomodgraph mermaid example.com/lib

Each line of "go mod graph" is "from to", meaning that from requires to. The main module has no version, and the other modules are written as path@version. Lines for the go and toolchain versions are skipped.

The edges are loaded as given, from -> to, so the dependencies of a module are the vertices it can reach, and the modules that depend on it are the vertices that can reach it. This is the opposite of a build graph, where an edge A -> B means A has to happen before B.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL