Directed Acyclic Graph (DAG) for pipeline dependency management.
Builds a dependency graph from groups and steps, supports cycle detection, and computes transitive dependencies (ancestors).
Example
groups = [
%Pipette.Group{name: :lint, steps: []},
%Pipette.Group{name: :test, depends_on: :lint, steps: []},
%Pipette.Group{name: :deploy, depends_on: :test, steps: []}
]
graph = Pipette.Graph.from_groups(groups)
Pipette.Graph.acyclic?(graph) #=> true
Pipette.Graph.ancestors(graph, :deploy) #=> MapSet.new([:test, :lint])
Summary
Functions
Return true if the graph has no cycles.
Add a directed edge from from to to. Both nodes are added if not present.
Add a node to the graph. Returns the graph unchanged if the node already exists.
Compute the transitive dependencies (ancestors) of a node.
Return all edges as a list of {from, to} tuples.
Find a cycle in the graph, if one exists.
Build a dependency graph from a list of groups.
Check if a directed edge exists from from to to.
Check if a node exists in the graph.
Create an empty graph.
Types
Functions
Return true if the graph has no cycles.
Add a directed edge from from to to. Both nodes are added if not present.
Add a node to the graph. Returns the graph unchanged if the node already exists.
Compute the transitive dependencies (ancestors) of a node.
Follows edges recursively to find all nodes that node depends on,
directly or transitively.
Examples
graph = Pipette.Graph.from_groups([
%Pipette.Group{name: :lint, steps: []},
%Pipette.Group{name: :test, depends_on: :lint, steps: []},
%Pipette.Group{name: :deploy, depends_on: :test, steps: []}
])
Pipette.Graph.ancestors(graph, :deploy)
#=> MapSet.new([:test, :lint])
Return all edges as a list of {from, to} tuples.
Find a cycle in the graph, if one exists.
Returns a list of nodes forming the cycle path, or nil if the graph
is acyclic. Uses DFS with three-color marking.
@spec from_groups([Pipette.Group.t()]) :: t()
Build a dependency graph from a list of groups.
Creates nodes for each group and step, with edges representing
depends_on relationships at both the group and step level.
Examples
groups = [
%Pipette.Group{name: :lint, steps: []},
%Pipette.Group{name: :test, depends_on: :lint, steps: []}
]
graph = Pipette.Graph.from_groups(groups)
Pipette.Graph.has_edge?(graph, :test, :lint) #=> true
Check if a directed edge exists from from to to.
Check if a node exists in the graph.
@spec new() :: t()
Create an empty graph.