godep-cruiser

command module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: MIT Imports: 3 Imported by: 0

README

godep-cruiser

Validate dependency rules for Go source trees.

godep-cruiser is a clean-room Go reimplementation of the concepts in dependency-cruiser by Sander Verweij — forbidden/allowed/required rules with regex path / pathNot matching, transitive reachability and package fan-in checks, dependency-type classification (stdlib / in-module / third-party / unresolved), and a violation baseline. It adds one thing the original does not have: stale baseline entries fail the run, so grandfathered exceptions expire automatically when the violation they cover disappears.

No code is translated from dependency-cruiser; the design derives from its public documentation and observable behavior. Not affiliated with the upstream project.

Quick start

Install the command from the module root:

go install github.com/butaosuinu/godep-cruiser@latest

Save a rule configuration as godep-cruiser.json (the complete example in Configuration is valid), then validate the current module:

godep-cruiser --config godep-cruiser.json --scan-root .

Human-readable err output is the default. JSON and Mermaid are selected explicitly:

godep-cruiser --config godep-cruiser.json --scan-root . --output-type json
godep-cruiser --config godep-cruiser.json --scan-root . --output-type mermaid

In JSON reports, violations[].kind is one of:

  • forbidden for an ordinary forbidden-rule match, including source-only orphan, package-name, and dependent-count checks
  • not-in-allowed for a dependency that matches no allowed rule
  • required for a source file missing a required import
  • reachable for a matching package that is transitively reachable
  • unreachable for a matching package outside the entry-point closure

Source-only violations serialize to as null; edge violations include the target dependency or package.

Generate and then apply an exact-match baseline:

godep-cruiser --config godep-cruiser.json --scan-root . \
  --generate-baseline > godep-cruiser-baseline.json

godep-cruiser --config godep-cruiser.json --scan-root . \
  --baseline godep-cruiser-baseline.json

Validation exits with the number of unsuppressed error violations plus stale baseline entries, capped at 255 so every failing validation stays non-zero as a process status. Warnings and informational violations are still reported but do not make the command fail. Flag, configuration, scan, and output failures exit 2; successful baseline generation exits 0.

Why

Go's compiler forbids import cycles but says nothing about architecture: layer direction, stdlib purity of a core package, or a tools tree that must stay dependency-free. Existing Go tools each miss part of that space (no stdlib restriction, no file-level exceptions, no fail-closed classification, no self-expiring exceptions). godep-cruiser targets that gap with a rules model proven by dependency-cruiser.

Configuration

v0.2 configuration is JSON-only so the runtime remains standard-library-only. The published JSON Schema describes every accepted field; the loader also validates Go regular expressions, numeric capture references, unknown fields, and source positions.

{
  "forbidden": [
    {
      "name": "features-stay-independent",
      "severity": "error",
      "from": {
        "path": ["^internal/features/([^/]+)/"]
      },
      "to": {
        "path": ["^internal/features/"],
        "pathNot": ["^internal/features/$1/"],
        "dependencyTypes": ["local"]
      }
    },
    {
      "name": "entrypoints-reach-production",
      "severity": "error",
      "from": {
        "path": ["^cmd/"]
      },
      "to": {
        "path": ["^internal/"],
        "pathNot": ["^internal/testutil(/|$)"],
        "reachable": false
      }
    },
    {
      "name": "shared-packages-have-multiple-dependents",
      "severity": "warn",
      "from": {
        "path": ["^internal/shared/"],
        "numberOfDependentsLessThan": 2
      },
      "to": {}
    }
  ],
  "required": [
    {
      "name": "services-require-logging",
      "severity": "error",
      "from": {
        "path": ["^internal/services/"]
      },
      "to": {
        "path": ["^internal/logging$"],
        "dependencyTypes": ["local"]
      }
    }
  ],
  "allowed": [
    {
      "name": "allow-resolved-dependencies",
      "from": {},
      "to": {
        "dependencyTypes": ["stdlib", "local", "module"]
      }
    }
  ],
  "allowedSeverity": "error"
}

from.path capture groups can be referenced as $1, $2, and later numeric references in to.path and to.pathNot. See DESIGN.ja.md for the matching and validation semantics.

Each required rule checks every file matching from and reports one source-only violation when none of that file's imports matches to. An importless matching file therefore violates the rule. from: {} is a catch-all; to: {} and from.orphan are invalid for required rules.

A forbidden rule can set to.reachable to evaluate the local package graph. true reports each matching target package reachable from a matching file's local imports; the diagnostic line identifies the import that starts the path. false treats packages containing matching files as entry points and reports every file in a matching target package outside their transitive closure. Both forms require to.path, allow to.pathNot, and reject dependency-type fields. Capture references remain available for true but are invalid for false; allowed and required rules do not accept reachable.

from.numberOfDependentsLessThan and from.numberOfDependentsMoreThan compare the source package's distinct direct local dependents with strict < and > bounds. In a forbidden rule, combining either condition with to: {} reports one source-only violation per matching file. Required rules do not accept dependent-count conditions.

Library API

The public facade is importable independently of the CLI. Configuration load, scan, validation, optional baseline filtering, reporting, and error counting remain ordinary Go calls:

configuration, err := config.LoadFile("godep-cruiser.json")
if err != nil {
	return err
}
result, err := cruiser.Validate(configuration, cruiser.Options{ScanRoot: "."})
if err != nil {
	return err
}
if err := cruiser.WriteReport(os.Stdout, cruiser.OutputTypeErr, result); err != nil {
	return err
}
if result.ErrorCount() != 0 {
	return fmt.Errorf("dependency validation failed with %d errors", result.ErrorCount())
}

Import github.com/butaosuinu/godep-cruiser/config and github.com/butaosuinu/godep-cruiser/cruiser for the snippet above. Set Options.GoModPath when the module file is not <ScanRoot>/go.mod; no go.work or nested-module discovery is performed.

Baseline

A baseline is a strict JSON document containing exact violation keys:

{
  "entries": [
    {
      "rule": "features-stay-independent",
      "from": "internal/features/orders/service.go",
      "to": "example.com/project/internal/features/payments"
    },
    {
      "rule": "no-orphans",
      "from": "internal/legacy/unused.go"
    }
  ]
}

For an ordinary import edge, the key is rule + from + to, where to is the raw import path written in the Go source rather than a resolved path. A reachable: true violation has no single raw target import, so its to key is the module-relative target package path. Source-only violations such as orphan, package-name, dependent-count, required, and reachable: false rules omit to and match on the pair rule and from.

The baseline has three outcomes:

  • An unlisted current violation is reported with its configured severity; a baseline never upgrades it.
  • A matching current violation is known and suppressed.
  • An entry with no matching current violation is always a stale error whose diagnostic tells the user to remove the entry from the baseline.

Generated entries are sorted and deduplicated. Loading rejects unknown fields, empty keys, duplicate keys, and trailing JSON, but does not require referenced files or imports to still exist because stale entries may point to deleted source. Regex entries, //nolint directives, and date-based expiry are not supported; exact keys make stale detection deterministic, and entries expire when their violations disappear.

License

MIT

Documentation

Overview

Package main provides the module-root godep-cruiser command so `go install github.com/butaosuinu/godep-cruiser@latest` installs the CLI.

Directories

Path Synopsis
cmd
godep-cruiser command
Package main provides the cmd-path compatibility entry point for the godep-cruiser command.
Package main provides the cmd-path compatibility entry point for the godep-cruiser command.
Package config loads and validates godep-cruiser rule configuration.
Package config loads and validates godep-cruiser rule configuration.
Package cruiser validates dependency rules for Go source trees.
Package cruiser validates dependency rules for Go source trees.
internal
baseline
Package baseline records known dependency violations and reports entries that no longer match a current violation.
Package baseline records known dependency violations and reports entries that no longer match a current violation.
cli
Package cli implements the shared godep-cruiser command-line runner.
Package cli implements the shared godep-cruiser command-line runner.
engine
Package engine evaluates dependency rules against scanner output.
Package engine evaluates dependency rules against scanner output.
graph
Package graph builds package-level dependency views from scanner output.
Package graph builds package-level dependency views from scanner output.
reporter
Package reporter renders rule-engine violations and stale baseline entries in stable output formats.
Package reporter renders rule-engine violations and stale baseline entries in stable output formats.
scanner
Package scanner builds a file-level import graph from Go source files.
Package scanner builds a file-level import graph from Go source files.
testcorpus
Package testcorpus loads the repository's engine-independent violation corpus.
Package testcorpus loads the repository's engine-independent violation corpus.

Jump to

Keyboard shortcuts

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