dependencies

package
v1.225.0 Latest Latest
Warning

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

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

Documentation

Overview

Package dependencies builds and renders the Atmos component dependency graph for the `atmos list dependencies` command. Unlike the execution-ordering graph used by the scheduler (which rejects cycles), this builder is cycle-tolerant so the command can visualize circular dependencies instead of failing on them.

Index

Constants

View Source
const (

	// FormatLevels renders each node's shortest graph distance from a selected root.
	FormatLevels = "levels"
)

Variables

This section is empty.

Functions

func BuildGraph

func BuildGraph(stacks map[string]any) (*dependency.Graph, error)

BuildGraph constructs a cycle-tolerant dependency graph from the described stacks map. It adds a node for every concrete (non-abstract, enabled) terraform component and an edge for every component-to-component dependency declared via either `dependencies.components` (preferred) or the legacy `settings.depends_on`. Edges to targets that are not present in the graph (e.g. disabled or filtered-out components) are skipped.

func ClosureScope added in v1.225.0

func ClosureScope(includeDependencies, includeDependents int) (Direction, Depths)

ClosureScope maps the flag-encoded closure depths (0 = off, -1 = unlimited, N>0 = N levels; see flags.ParseClosureDepth) onto a traversal Direction and per-direction Depths (filter encoding: 0 = unlimited). At least one of the two values must be non-zero; when both are set the direction is DirectionBoth with independent depths.

func ComponentNames added in v1.225.0

func ComponentNames(graph *dependency.Graph) []string

ComponentNames returns the sorted, deduplicated component names present in graph. Used by `list components` to keep exactly the components a closure selects.

func Membership added in v1.225.0

func Membership(graph *dependency.Graph) map[string]struct{}

Membership returns the set of (component, stack) node IDs present in graph, keyed by NodeID. Row-shaped consumers (e.g. `list instances`) use it to keep exactly the rows a closure selects.

func NodeID

func NodeID(component, stack string) string

NodeID returns the canonical, collision-safe node ID for a component in a stack. It uses a length-prefixed encoding so that component/stack names containing the delimiter character never produce the same ID for distinct (component, stack) pairs (e.g. "app-prod"+"us" vs "app"+"prod-us").

func ReachableClosure added in v1.225.0

func ReachableClosure(graph *dependency.Graph, roots []string, direction Direction, depths Depths) *dependency.Graph

ReachableClosure returns the subgraph reachable from roots in the requested direction: forward follows Dependencies, reverse follows Dependents, both follows either — each bounded by the matching Depths field (0 = unlimited). It reuses Graph.Filter's existing IncludeDependencies/IncludeDependents traversal rather than re-implementing graph walking, so cross-stack edges (Node.Dependencies/Dependents already span stacks) are followed naturally. An empty roots list returns an empty graph.

func Render

func Render(graph *dependency.Graph, opts Options) (string, error)

Render produces the dependency output for the given graph and options.

func Roots added in v1.225.0

func Roots(graph *dependency.Graph, sel *Selector) []string

Roots returns the sorted IDs of nodes matching the selector, for use as ReachableClosure roots.

func StackNames added in v1.225.0

func StackNames(graph *dependency.Graph) []string

StackNames returns the sorted, deduplicated set of stack names present in graph. Used to scope a closure-limited full evaluation pass (templates/YAML functions/auth) to exactly the stacks a reachable closure touches, instead of every stack in the repo.

func UnresolvedDependencySources added in v1.225.0

func UnresolvedDependencySources(stacks map[string]any, leftDelim string) map[string][]string

UnresolvedDependencySources returns, per stack, the sorted components whose dependency declarations contain an unresolved (templated or YAML-function) component/stack value. BuildGraph drops such edges — the literal target ID matches no node. For the FORWARD direction the scoped-closure loop converges anyway: the declaring component is already in the closure, so Phase C evaluation resolves its edges. In the REVERSE direction the declaring component is exactly the node the closure is trying to discover, so it must be conservatively evaluated for its edges to materialize at all.

Types

type Depths added in v1.225.0

type Depths struct {
	// Dependencies bounds forward (what the roots depend on) expansion.
	Dependencies int
	// Dependents bounds reverse (what depends on the roots) expansion.
	Dependents int
}

Depths bounds closure expansion per direction, measured in dependency levels from the nearest root (0 = unlimited).

type DescribeFunc added in v1.225.0

type DescribeFunc func(stack string, components []string, processTemplates, processFunctions bool) (map[string]any, error)

DescribeFunc runs one describe-stacks pass. An empty stack means every stack; a non-nil components list narrows evaluation to those component names within the stack (nil = every component); processTemplates/ processFunctions control template and YAML-function evaluation for the pass. Callers must NOT narrow the describe by tags or labels — scoping is the closure's job, and a narrowed describe would drop dependency/dependent components from the graph before the closure could see them. The components list is supplied BY the closure engine (never by the caller's own selection) so that evaluating a closure stack does not evaluate unrelated components that merely share the stack.

type Direction

type Direction string

Direction selects which dependency edges to display.

const (
	// DirectionBoth shows both what a component depends on and what depends on it.
	DirectionBoth Direction = "both"
	// DirectionForward shows what a component depends on.
	DirectionForward Direction = "forward"
	// DirectionReverse shows what depends on a component (its dependents).
	DirectionReverse Direction = "reverse"
)

type Options

type Options struct {
	// Format is the output format: tree (default), json, yaml, or levels.
	Format string
	// Direction selects forward, reverse, or both edge directions.
	Direction Direction
	// Component optionally filters the top-level entries to a single component.
	Component string
	// Stack optionally filters the top-level entries to a single stack.
	Stack string
	// Tags optionally filters the top-level entries to components whose
	// metadata.tags contains at least one of these tags (any-match).
	Tags []string
	// Labels optionally filters the top-level entries to components whose
	// metadata.labels contains every key=value pair (all-match).
	Labels map[string]string
	// LeftDelim/RightDelim are the configured template delimiters ("{{"/"}}"
	// when empty), used to detect — and best-effort resolve — still-unresolved
	// selector templates under custom 'templates.settings.delimiters'
	// configurations.
	LeftDelim  string
	RightDelim string
}

Options configures dependency rendering.

type ScopeRequest added in v1.225.0

type ScopeRequest struct {
	// Components optionally narrows the seed to these component names.
	Components []string
	// Stack optionally narrows the seed to a single stack.
	Stack string
	// Tags narrows the seed to components whose metadata.tags match (any-match).
	Tags []string
	// Labels narrows the seed to components whose metadata.labels match (all-match).
	Labels map[string]string
	// Direction selects forward (dependencies), reverse (dependents), or both.
	Direction Direction
	// Depths bounds the closure expansion per direction (0 = unlimited).
	Depths Depths
	// ProcessTemplates/ProcessFunctions control evaluation in the resolved
	// (Phase C) describe passes; the lightweight pass always runs with both off.
	ProcessTemplates bool
	ProcessFunctions bool
	// LeftDelim/RightDelim are the configured template delimiters ("{{"/"}}"
	// when empty), used to detect — and best-effort resolve — templated
	// selectors in the lightweight graph.
	LeftDelim  string
	RightDelim string
}

ScopeRequest bounds a scoped closure resolution: the selection seed (components/stack/tags/labels), the closure direction and depths, and the evaluation settings for the resolved passes.

type ScopeResult added in v1.225.0

type ScopeResult struct {
	// Stacks is the merged union of the resolved per-stack describe passes —
	// exactly the stacks the closure touches, fully evaluated. Feed this to
	// graph-backed execution (e.g. scheduleradapters.ExecuteTerraform).
	Stacks map[string]any
	// Closure is the final reachable closure computed against the resolved
	// graph. List commands use it to filter rows.
	Closure *dependency.Graph
}

ScopeResult is the outcome of a scoped closure resolution.

func ResolveScopedClosure added in v1.225.0

func ResolveScopedClosure(describe DescribeFunc, req *ScopeRequest) (*ScopeResult, error)

ResolveScopedClosure implements the three-phase scoped evaluation shared by `atmos list dependencies` and the terraform bulk commands:

  • Phase A: one lightweight structural describe across every stack (templates/YAML functions off — component identity plus dependencies.components/settings.depends_on edges, which are static in practice; see the fix doc's grep of examples/tests).
  • Phase B: seed roots from the request's selection filters, then compute the reachable closure in the requested direction(s) and depth(s).
  • Phase C: fully evaluate ONLY the stacks the closure touches, then recompute the closure against the resolved graph. If the resolved edges reveal additional stacks the lightweight pass could not see (e.g. a templated `stack:` dependency target), describe those too and recompute again — repeating until the touched-stack set stabilizes. This terminates in at most len(repo stacks) rounds, since each round only adds genuinely new, structurally-discovered stacks.

Known limitation: a dependency target `stack:` value templated to point at a DIFFERENT stack converges once that stack is described (the loop expands and redescribes until stable), but only within the stacks reachable that way — it cannot discover a target stack whose name depends on a value this pass never touches (e.g. a remote store lookup). The describe.settings.eager_evaluation setting remains the fallback to the historical full-repo-evaluation behavior.

Reverse/both directions additionally evaluate every component whose dependency declarations were unresolved in the lightweight pass (see UnresolvedDependencySources): a dependent with a templated edge would otherwise never be evaluated (it is not yet IN the closure) and thus never discovered. This is a conservative extra-evaluation cost, never an extra-execution cost — membership is still decided by the closure computed against the resolved graph.

type Selector added in v1.225.0

type Selector struct {
	Components []string
	Stack      string
	Tags       []string
	Labels     map[string]string
	LeftDelim  string
	RightDelim string
}

Selector bundles the seed-matching filters for root selection: components (empty = every component), a stack (exact name or path.Match glob — the `list` commands' --stack semantics), tags (any-match), labels (all-match), and the configured left template delimiter for detecting unresolved selectors. An empty filter matches every node, and a templated (unresolvable) tags/labels selector conservatively matches so a lightweight unevaluated graph can never wrongly exclude a root.

Jump to

Keyboard shortcuts

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