visualize

package
v0.14.0-dryrun.55 Latest Latest
Warning

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

Go to latest
Published: Jul 10, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package visualize builds a render-agnostic view of a cascade pipeline and emits it as a diagram. The view model carries no diagram syntax; concrete Emitter implementations (Mermaid is the first) turn it into renderer-specific source. The separation keeps the projection independently testable and lets a richer renderer be added later without reshaping the model.

Index

Constants

This section is empty.

Variables

View Source
var BlandTheme = Theme{
	Name:      "bland",
	Base:      "neutral",
	LineColor: "#8c959f",
	NodeStyles: map[NodeKind]NodeStyle{
		NodeValidate: {Fill: "#f6f8fa", Stroke: "#6e7781", Text: "#24292f"},
		NodeBuild:    {Fill: "#eaeef2", Stroke: "#6e7781", Text: "#24292f"},
		NodeDeploy:   {Fill: "#d0d7de", Stroke: "#57606a", Text: "#24292f"},
		NodeStage:    {Fill: "#eaeef2", Stroke: "#6e7781", Text: "#24292f"},
		NodeEnv:      {Fill: "#f6f8fa", Stroke: "#6e7781", Text: "#24292f"},
		NodeHotfix:   {Fill: "#d0d7de", Stroke: "#57606a", Text: "#24292f"},
		NodeRepo:     {Fill: "#d0d7de", Stroke: "#57606a", Text: "#24292f"},
	},
}

BlandTheme is a monotone grayscale palette for low-distraction or print contexts: every node kind sits on a light-gray fill with a gray border, and edges are a muted gray. It is intentionally distinct from CascadeTheme.

View Source
var CascadeTheme = Theme{
	Name:      "cascade",
	Base:      "base",
	LineColor: "#57606a",
	NodeStyles: map[NodeKind]NodeStyle{
		NodeValidate: {Fill: "#1f6feb", Stroke: "#0b3d91", Text: "#ffffff"},
		NodeBuild:    {Fill: "#2da44e", Stroke: "#116329", Text: "#ffffff"},
		NodeDeploy:   {Fill: "#8250df", Stroke: "#512a97", Text: "#ffffff"},
		NodeStage:    {Fill: "#bf8700", Stroke: "#7d4e00", Text: "#ffffff"},
		NodeEnv:      {Fill: "#1f6feb", Stroke: "#0b3d91", Text: "#ffffff"},
		NodeHotfix:   {Fill: "#cf222e", Stroke: "#82071e", Text: "#ffffff"},
		NodeRepo:     {Fill: "#6e7781", Stroke: "#424a53", Text: "#ffffff"},
	},
}

CascadeTheme is the branded default palette: a distinct accent per node kind over a mid-gray edge color. It is the theme cascade graph renders when no theme is requested.

View Source
var DefaultTheme = CascadeTheme

DefaultTheme is the theme used when a caller does not request one. It is the branded cascade palette, so an unparameterized emit is styled by default.

Functions

This section is empty.

Types

type DiagramKind

type DiagramKind string

DiagramKind tells an emitter which family of diagram a ViewModel describes. The job and stage projections are directed graphs (a flowchart); the env projection is a promotion state machine (a state diagram). The kind lives on the model, not in the emitter, so the emitter stays a thin renderer that switches on what the model already declares rather than on the granularity.

const (
	DiagramFlowchart DiagramKind = "flowchart"
	DiagramState     DiagramKind = "state"
)

Diagram kinds. The zero value renders as a flowchart so a model built without setting Kind (the original job projection) keeps its behavior.

type Edge

type Edge struct {
	From  string
	To    string
	Kind  EdgeKind
	Label string
}

Edge is one connection from a source node to a target node. From and To are node IDs, Kind selects the visual style, and Label is an optional transition caption an emitter renders when the renderer supports edge labels (the env state machine uses it for promote, diverge, and rejoin). An empty Label emits no caption, matching the unlabeled job and stage edges.

type EdgeKind

type EdgeKind string

EdgeKind classifies an edge. Hard edges come from Edges (they both order a job and skip-gate it); optional edges come from OptionalEdges (they order only). Stage edges connect lifecycle stages. Promote, diverge, and rejoin edges are transitions in the env state machine. Emitters render the kinds with visually distinct styling so a reader can tell them apart.

const (
	EdgeHard     EdgeKind = "hard"
	EdgeOptional EdgeKind = "optional"
	// EdgeStage connects two lifecycle stages in the stages projection.
	EdgeStage EdgeKind = "stage"
	// EdgePromote is a promotion transition between two consecutive envs.
	EdgePromote EdgeKind = "promote"
	// EdgeDiverge is the transition from an env onto its hotfix branch.
	EdgeDiverge EdgeKind = "diverge"
	// EdgeRejoin is the transition from a hotfix branch back onto the ladder.
	EdgeRejoin EdgeKind = "rejoin"
	// EdgeTransition is an unlabeled state-machine transition, used for the
	// start and end bookend edges.
	EdgeTransition EdgeKind = "transition"
	// EdgeExternal is the cross-repo coordination edge from the primary's
	// pipeline to a dependent satellite's deployable, labeled with the deploy
	// the primary drives.
	EdgeExternal EdgeKind = "external"
	// EdgeNotify is the cross-repo notify edge from a satellite's pipeline back to
	// the primary it informs after a dev deploy.
	EdgeNotify EdgeKind = "notify"
)

Edge kinds.

type Emitter

type Emitter interface {
	// Emit renders vm using theme and any options, returning the diagram source.
	// It returns an error only when the model cannot be expressed as valid
	// source; a well-formed model always emits successfully.
	Emit(vm ViewModel, theme Theme, opts ...Option) (string, error)
}

Emitter turns a render-agnostic ViewModel into diagram source. It is the pluggable seam of the package: Mermaid is the first implementation, and a future renderer is added by writing another Emitter without touching the view model. Required inputs (the model and a theme) are positional; optional behavior arrives as a variadic Option tail.

type Group

type Group struct {
	ID      string
	Label   string
	NodeIDs []string
}

Group is a named lane that visually clusters a subset of nodes, used by the cross-repo projection to draw one lane per repository. ID is the stable lane identity, Label is the human-facing caption, and NodeIDs lists the member node IDs in render order. An emitter that has no notion of grouping (or a model with no groups) ignores it, so grouping is additive and never reshapes the existing flat projections.

type MermaidEmitter

type MermaidEmitter struct{}

MermaidEmitter renders a ViewModel as Mermaid flowchart source that GitHub renders natively in Markdown. It is the first concrete Emitter. Output is deterministic: it ranges the model's ordered slices and never iterates a map, so the same model always yields byte-identical source.

func NewMermaidEmitter

func NewMermaidEmitter() *MermaidEmitter

NewMermaidEmitter returns a ready MermaidEmitter. The type is stateless, so the constructor exists only to give callers a stable construction point.

func (MermaidEmitter) Emit

func (MermaidEmitter) Emit(vm ViewModel, theme Theme, opts ...Option) (string, error)

Emit renders vm as Mermaid source. The diagram family follows the model's Kind: a state machine (env projection) becomes a stateDiagram-v2, every other model becomes a top-down flowchart. theme styles the output: it is rendered into a leading init directive and trailing classDef and class lines, so a theme swap restyles the header block while leaving the structure unchanged. A title option, if set, is emitted as a Mermaid title in the frontmatter block.

type Node

type Node struct {
	ID    string
	Label string
	Kind  NodeKind
}

Node is one pipeline job in the view. ID is the stable, prefixed job ID (validate, build-app, deploy-app) used as the diagram node identity. Label is the human-facing display name. Kind drives styling.

type NodeKind

type NodeKind string

NodeKind classifies a node for rendering. The first three mirror the callback types cascade models (validate, build, deploy) for the job DAG. NodeStage is a coarse lifecycle stage; NodeEnv, NodeHotfix, NodeStart, and NodeEnd describe the env state machine. It is a rendering hint only; an emitter may map several kinds to one visual style.

const (
	NodeValidate NodeKind = "validate"
	NodeBuild    NodeKind = "build"
	NodeDeploy   NodeKind = "deploy"
	// NodeStage is one coarse lifecycle stage in the stages projection.
	NodeStage NodeKind = "stage"
	// NodeEnv is one environment state in the env promotion ladder.
	NodeEnv NodeKind = "env"
	// NodeHotfix is a diverged integration-branch state hanging off an env.
	NodeHotfix NodeKind = "hotfix"
	// NodeStart marks the state-machine entry point. An emitter renders it as the
	// renderer's initial pseudo-state rather than a declared node.
	NodeStart NodeKind = "start"
	// NodeEnd marks the state-machine terminal. An emitter renders it as the
	// renderer's final pseudo-state rather than a declared node.
	NodeEnd NodeKind = "end"
	// NodeRepo is another repository rendered as an opaque node in the cross-repo
	// projection, used for an upstream primary a satellite notifies or a
	// dependent repo that declares no individual deployables.
	NodeRepo NodeKind = "repo"
)

Node kinds.

type NodeStyle

type NodeStyle struct {
	// Fill is the node background color, for example "#1f6feb".
	Fill string `json:"fill,omitempty"`
	// Stroke is the node border color.
	Stroke string `json:"stroke,omitempty"`
	// Text is the node label color.
	Text string `json:"text,omitempty"`
}

NodeStyle holds the presentation attributes an emitter renders for one node kind. Each field maps to a Mermaid classDef attribute (fill, stroke, color); an empty field is omitted so a theme can style only the slots it cares about.

type Option

type Option func(*Options)

Option configures emit behavior. Following the repo convention, required inputs are positional on Emit and optional behavior arrives as a variadic Option tail, so capabilities can be added without changing the signature.

func WithTitle

func WithTitle(title string) Option

WithTitle sets a diagram heading. Emitters that have no notion of a heading ignore it.

type Options

type Options struct {
	// Title, when set, is rendered as a diagram heading by emitters that
	// support one. An empty title emits no heading.
	Title string
}

Options holds optional emit behavior. It is populated by the functional Option tail rather than constructed directly, so new knobs are additive. The zero value is valid and selects each emitter's defaults.

type Theme

type Theme struct {
	// Name identifies the theme. The built-in values use "cascade" and "bland";
	// a user-supplied theme file sets its own.
	Name string `json:"name"`
	// Base selects the Mermaid base theme rendered in the init directive (for
	// example "base" or "neutral"). Empty omits the directive unless LineColor is
	// set, in which case it falls back to "base".
	Base string `json:"base,omitempty"`
	// LineColor styles edges and transitions through the init directive's
	// themeVariables. Empty leaves edge color to the renderer default.
	LineColor string `json:"lineColor,omitempty"`
	// NodeStyles maps a NodeKind to its classDef style. A kind with no entry, or
	// an empty entry, renders without a class so the renderer default applies.
	NodeStyles map[NodeKind]NodeStyle `json:"nodeStyles,omitempty"`
}

Theme carries the styling the Mermaid emitter renders into a diagram's header. It is render data only: the emitter turns it into an init directive and classDef lines, and the view model never sees it, so a theme swap restyles a diagram without reshaping the model. The zero value emits an unstyled diagram, so the field is always safe to thread through an emit path.

func LoadTheme

func LoadTheme(path string) (Theme, error)

LoadTheme reads a user-supplied theme definition from a JSON file and returns it. It surfaces a clear error when the file cannot be read, does not parse as JSON, or omits the required name, so a malformed theme fails fast rather than rendering a silently broken diagram.

func LookupTheme

func LookupTheme(name string) (Theme, bool)

LookupTheme returns the built-in theme registered under name. "default" is an alias for the cascade theme so existing callers keep working. The bool is false for any name that is not a built-in, signalling the caller to treat the value as a theme-file path instead.

type ViewModel

type ViewModel struct {
	Kind   DiagramKind
	Nodes  []Node
	Edges  []Edge
	Groups []Group
}

ViewModel is the deterministic, render-agnostic description of one pipeline projection. Kind tells the emitter which diagram family to render; Nodes and edges follow a stable construction order so two builds of the same manifest produce byte-identical emitter output. Groups, when present, cluster nodes into named lanes (the cross-repo projection draws one lane per repo); a model with no groups renders flat as before. The model holds no diagram syntax.

func BuildCrossRepoViewModel

func BuildCrossRepoViewModel(cfg *config.TrunkConfig) (ViewModel, error)

BuildCrossRepoViewModel projects the manifest's cross-repo coordination into a render-agnostic flowchart. The primary repo's lifecycle pipeline forms the root lane; each dependent satellite the primary coordinates (cfg.External) becomes its own lane holding a node per external deployable, with an edge from the primary's pipeline to each one labeled with the deploy it drives; and a notify config (cfg.Notify) adds an upstream-primary lane with a notify edge back to it. A manifest with no external coordination and no notify renders just the primary pipeline with no lanes, so the projection stays sensible for the common single-repo case. The model carries no diagram syntax; the emitter draws the lanes and the cross-repo edges.

func BuildEnvViewModel

func BuildEnvViewModel(cfg *config.TrunkConfig, state map[string]*config.EnvState) (ViewModel, error)

BuildEnvViewModel projects the promotion ladder into a render-agnostic state machine. The configured environments become a linear chain of env states from the first environment to the last, bracketed by a start and an end marker. Each environment whose state has diverged onto an integration branch (a hotfix Ref or applied patches) gains a hotfix state that branches off that environment and rejoins the ladder at the next environment downstream, or at the terminal when the diverged environment is last. The model carries no diagram syntax; the emitter turns it into a state diagram.

func BuildStagesViewModel

func BuildStagesViewModel(cfg *config.TrunkConfig) (ViewModel, error)

BuildStagesViewModel projects the manifest into the coarse lifecycle flow: trunk, build, deploy, promote, release. It collapses every callback into its stage, so the diagram shows the shape of the pipeline without per-callback detail. A stage appears only when the manifest exercises it: build when the manifest declares builds, deploy when it declares deploys, and promote when it declares more than one environment. Trunk and release always frame the flow. The present stages are chained in lifecycle order with a single edge between each consecutive pair. The model carries no diagram syntax.

func BuildViewModel

func BuildViewModel(g *generate.DependencyGraph) (ViewModel, error)

BuildViewModel projects a generated DependencyGraph into a render-agnostic ViewModel. It walks the graph in Order (the same deterministic seed TopologicalSort uses) so node and edge slices are stable run to run, and it rejects a cyclic graph by surfacing the cycle TopologicalSort detects rather than emitting a malformed diagram. The returned model depends only on the cascade model and carries no Mermaid (or other) syntax.

Jump to

Keyboard shortcuts

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