Documentation
¶
Index ¶
- Constants
- func IsStdlib(importPath string) bool
- func Scopes() []string
- func WriteResult(out io.Writer, result Result, format Format) error
- type Classification
- type Diagnostic
- type Effective
- type Expectation
- type ExpectationResult
- type Explanation
- type Finding
- type ForbidEdge
- type Format
- type Group
- type Layers
- type Mode
- type Module
- type Pattern
- type PatternMatch
- type Policy
- type Reference
- type RepoConfig
- type RepoType
- type Result
- type RoleRule
- type Scope
- type ScopeVerdict
- type Source
- type SourceKind
Constants ¶
const ( // RuleImport is a dependency this kind of repository may not have. RuleImport = "import" // RuleLayer is an import travelling the wrong way inside one repository. RuleLayer = "layer" // RuleRole is a package whose name matches no declared role, reported // only where the policy asks for it. RuleRole = "role" )
Rule names the family a finding belongs to.
const ( ScopeSource = "source" ScopeTests = "tests" // ScopeMain is a composition root — a file in package main. Wiring a // concrete database driver or bot framework is what a composition root is // for, and forbidding it there would either flag every daemon in the fleet // or force the wiring somewhere worse. ScopeMain = "main" )
Scope names the body of code a rule applies to. Test files legitimately reach for dependencies production code must not — an in-memory or emulator database driver being the usual case — so the two are separate rule sets declared centrally, rather than an exception filed per repository.
const ConfigFileName = ".wb-deps-policy.yaml"
ConfigFileName is the per-repository policy declaration. It is deliberately tiny: which policy governs this repository, and — only where the module path cannot say — what kind of repository it is.
const GroupStdlib = "stdlib"
GroupStdlib is assigned to imports of the Go standard library. The standard library is never an architecture boundary, so it is always permitted and cannot be named in an allow list.
const GroupUnclassified = "unclassified"
GroupUnclassified is assigned to an import no declared group matches. Rules are allow lists, so an unclassified import is denied: a policy that gains a new kind of dependency fails closed instead of quietly permitting it. A policy that wants a catch-all declares one, with the pattern "...".
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Classification ¶
type Classification struct {
Import string
Group string
// Pattern is the winning pattern as the policy author wrote it, and
// PatternNumber its position in declaration order across the whole policy.
Pattern string
PatternNumber int
// AlsoMatched lists patterns declared after the winner that would also
// have matched. Under first-match-wins these are shadowed, and being able
// to see them is what makes an ordering mistake findable.
AlsoMatched []PatternMatch
}
Classification is the full account of how one import path was classified. It carries more than the answer because the answer alone is not reviewable: when a verdict surprises someone, what they need is which pattern won and what else would have matched.
type Diagnostic ¶
type Diagnostic struct {
Message string
}
Diagnostic is a non-fatal finding about a policy document itself.
func Validate ¶
func Validate(policy Policy) []Diagnostic
Validate reports quality problems in a policy that do not stop it running but do stop it meaning what its author thinks it means.
The one that matters most is an unreachable group pattern. Classification is first-match-wins, so a broad pattern placed above a narrow one silently takes every path the narrow one was written for, and nothing anywhere errors.
type Effective ¶
type Effective struct {
PolicySource string
Module string
RepoType string
TypeDetected bool
ConfigPath string
Strict bool
Scopes []ScopeVerdict
LayerMode Mode
LayerOrder string
LayerForbid []ForbidEdge
}
Effective is the resolved rule set one repository is actually held to.
type Expectation ¶
Expectation is one assertion a policy makes about itself, exercised by `wb deps policy test`. Classification is the part of a policy that breaks quietly, so a policy is expected to carry examples of its own intent.
type ExpectationResult ¶
type ExpectationResult struct {
Expectation Expectation
Subject string
Want string
Got string
Passed bool
Err string
}
ExpectationResult is the outcome of one policy self-assertion.
func RunExpectations ¶
func RunExpectations(policy Policy) []ExpectationResult
RunExpectations exercises the assertions a policy makes about itself.
Classification is the part of a policy that breaks quietly — reorder two group patterns and every verdict downstream changes with nothing to show for it — so a policy is expected to carry examples of what it means.
type Explanation ¶
type Explanation struct {
Import string
Module string
RepoType string
TypeDetected bool
Classification Classification
// Scopes reports the verdict in each scope, because the same import can
// be legitimate in a test and forbidden in production code.
Scopes []ScopeVerdict
}
Explanation is the full account of one classification decision, which is what someone needs when a verdict surprises them.
type Finding ¶
type Finding struct {
Rule string
Mode Mode
File string
Line int
Package string
Scope string
Import string
Manifest bool
// Group is set for import findings.
Group string
// FromRole and ToRole are set for layer findings.
FromRole string
ToRole string
Message string
// Fix names the shape of the remedy where one can be stated honestly.
Fix string
}
Finding is one violation.
type ForbidEdge ¶
ForbidEdge is one explicitly refused role-to-role import.
type Format ¶
type Format string
Format is an output rendering.
const ( // FormatText is for a person reading a terminal. FormatText Format = "text" // FormatJSON is for another program, including fleet aggregation. FormatJSON Format = "json" // FormatGitHub emits workflow commands so findings land as annotations on // the changed lines of a pull request. FormatGitHub Format = "github" )
func ParseFormat ¶
ParseFormat validates a format name.
type Group ¶
Group classifies import paths. Order is significant and first match wins, so a narrow group must be declared above a broad one.
type Layers ¶
type Layers struct {
Mode Mode
// UnknownRole says what to do with a package whose name matches no role.
// "ignore" is the sane default while a fleet still has packages that
// predate the convention.
UnknownRole string
Roles []RoleRule
// Order runs from the outermost layer to the innermost. A package may
// import its own layer and any layer below it, never above.
Order [][]string
// Forbid names individual role edges that are refused even though the
// layer order permits them. The depth rule alone cannot express "delivery
// must go through the facade", because api → dal does travel downward;
// stating such edges explicitly keeps the exception visible in the policy
// rather than hidden in the tool.
Forbid []ForbidEdge
}
Layers describes permitted direction between packages inside one repository.
type Mode ¶
type Mode string
Mode says whether a rule family blocks or merely reports. It is declared in the central policy and cannot be set by a repository, so a new rule can be rolled out fleet-wide without any repository being able to opt itself out.
type Module ¶
type Module struct {
Path string
Dir string
References []Reference
// Unparseable lists files that could not be parsed. They are reported
// rather than swallowed: a file the scanner cannot read is a hole in the
// check, and a hole that stays quiet is worse than one that does not.
Unparseable []string
}
Module is the lexical evidence gathered from one Go module.
Nothing here is resolved or type-checked: the scan reads import blocks and go.mod, and never downloads a dependency. That is deliberate — it means the check still reports when the build itself cannot start, which is exactly when an architecture boundary is most likely to be under discussion.
func ScanModule ¶
ScanModule reads the module rooted at dir.
type Pattern ¶
type Pattern struct {
// contains filtered or unexported fields
}
Pattern matches Go import paths and module paths.
The syntax is deliberately the one Go developers already read:
github.com/acme/thing exactly that path
github.com/acme/thing/... that path and everything beneath it
github.com/acme/ext-*/... "*" matches within one path segment
github.com/acme/{a,b}/... brace alternation
<self>/... the module being scanned
Braces are expanded at compile time, so a pattern is held as one or more flat alternatives.
func CompilePattern ¶
CompilePattern parses one pattern. It reports an error rather than silently accepting a pattern that cannot match anything, because an unmatchable pattern in a policy is invisible at check time.
func (Pattern) Covers ¶
Covers reports whether every path described by other is also described by p — the check behind the "unreachable pattern" diagnostic in validate.
It is a deliberately conservative approximation: for each of other's alternatives it builds one representative concrete path and asks whether p matches it. That catches the ordering mistake this exists for (a broad pattern placed above a narrow one), and errs towards staying quiet rather than reporting a shadow that is not real.
type PatternMatch ¶
PatternMatch names one pattern that matched.
type Policy ¶
type Policy struct {
// Source records where the document was loaded from, so `show` can tell a
// repository what it is actually being held to.
Source string
Groups []Group
Types []RepoType
Layers Layers
Expectations []Expectation
}
Policy is a complete, compiled rule set. It carries no knowledge of any particular fleet: every name in it comes from the policy document.
func Load ¶
Load reads and compiles a policy document.
It fails on anything that would make the policy unusable or silently wrong: a malformed pattern, a duplicate name, an allow list naming a group that does not exist, a type with nothing to detect it by. Softer quality findings — a pattern another pattern already shadows, a role nobody placed in the layer order — are reported by Validate instead, so that they can be surfaced without refusing to run.
func (Policy) Classify ¶
func (p Policy) Classify(importPath, self string) Classification
Classify assigns an import path to a group. self is the module path being scanned, substituted for <self> patterns.
func (Policy) Detect ¶
Detect resolves a module path to a repository type.
Types are declared in order and the first match wins — the same rule groups follow, so a policy document has one ordering principle rather than two. Overlap is expected and useful: "github.com/acme/ext-*/backend" above "github.com/acme/*/backend" reads as "contracts, then everything else", which is what the author means. Validate reports a detect pattern that an earlier type has already claimed entirely.
func (Policy) GroupNames ¶
GroupNames lists declared group names in declaration order.
type Reference ¶
type Reference struct {
// Import is the imported path, or the required module path for a manifest
// requirement.
Import string
// File is slash-separated and relative to the module directory.
File string
// Line is 1-indexed; 0 for a manifest requirement with no useful position.
Line int
// Package is the directory the importing file lives in, relative to the
// module directory. Empty means the module root.
Package string
// Scope is source or tests.
Scope string
// Manifest marks a go.mod requirement rather than a source import.
Manifest bool
}
Reference is one dependency edge found in a module: an import in a source file, or a requirement in go.mod.
type RepoConfig ¶
type RepoConfig struct {
// Found is false when the repository has no config file at all.
Found bool
// Path is where the config was read from.
Path string
Policy string
Type string
// Strict promotes report-mode rules to errors for this repository only.
// It is the single permitted local change, and it can only tighten.
Strict bool
}
RepoConfig is a repository's declaration.
func LoadRepoConfig ¶
func LoadRepoConfig(root string) (RepoConfig, error)
LoadRepoConfig reads the config file in root. A missing file is not an error: detection from the module path is the normal case, and a repository with nothing to say should not need to say it.
type RepoType ¶
RepoType is a kind of repository. Detect patterns are matched against the module path, which is why most repositories need no configuration at all.
type Result ¶
type Result struct {
Module Module
Policy Policy
// Type is the repository type the rules were taken from.
Type string
// TypeDetected records whether Type came from detection or was declared.
TypeDetected bool
Findings []Finding
}
Result is the outcome of checking one module.
func Check ¶
Check applies a policy to a scanned module.
declaredType overrides detection. It exists because a module path cannot always say what a repository is; it is not an escape hatch, since the rules it selects are still the central policy's.
func (*Result) ApplyStrict ¶
func (r *Result) ApplyStrict()
ApplyStrict promotes report-mode findings to blocking ones. A repository may hold itself to more than the fleet requires; it may never hold itself to less.
func (Result) Blocking ¶
Blocking counts findings that must fail the command. Report-mode findings are excluded: they are visible and counted, and they do not gate.
type Scope ¶
type Scope struct {
Allow []string
}
Scope is the allow list for one body of code. There is deliberately no deny list: anything not allowed is forbidden, which leaves nothing to widen.
type ScopeVerdict ¶
ScopeVerdict is the outcome for one scope.
type Source ¶
Source is a parsed policy reference.
func ParseSource ¶
ParseSource reads a policy reference.
A reference names which policy applies and never which release of it. A repository frozen on an old policy would be carrying an exception without anyone having written one down, so a pinned version is refused here rather than honoured.
type SourceKind ¶
type SourceKind string
SourceKind is how a policy reference is resolved.
const ( // SourcePath is a file beside the repository. SourcePath SourceKind = "path" // SourceFleet is owner/repo//path, resolved against local checkouts. SourceFleet SourceKind = "fleet" // SourceURL is an https document. SourceURL SourceKind = "url" )