Skip to content
| Marketplace
Sign in
Visual Studio Code>Visualization>GitLab Pipeline VisualizerNew to Visual Studio Code? Get it now.
GitLab Pipeline Visualizer

GitLab Pipeline Visualizer

Jorge Cardona

|
23 installs
| (0) | Free
Visualize .gitlab-ci.yml pipelines as interactive graphs
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

GitLab Pipeline Visualizer

VS Code extension that visualizes .gitlab-ci.yml pipelines as interactive graphs.

Features

  • Sidebar tree view — stages as expandable parents, jobs as children with typed icons (build/test/deploy/security/manual)
  • Interactive graph — dagre-laid-out DAG with colored stage columns, zoom/pan/fit, click jobs for details
  • Live updates — re-renders as you edit your .gitlab-ci.yml (works with unsaved changes)
  • GitLab CI support — handles needs, extends, include:local, rules, when: manual, !reference tags, environments, artifacts

Usage

  1. Open a workspace containing a .gitlab-ci.yml file
  2. The Pipeline Structure sidebar populates automatically
  3. Click the graph icon in the sidebar title bar to open the interactive graph
  4. Edit your .gitlab-ci.yml and watch the visualization update live

Commands

  • GitLab Pipeline: Show Graph — open the pipeline graph panel
  • GitLab Pipeline: Refresh — force re-parse the CI file
  • GitLab Pipeline: Select CI File — choose which .gitlab-ci.yml to visualize

Development

bun install
bun run build    # production build
bun run watch    # dev build with watch mode

Press F5 to launch the Extension Development Host for testing.


Layout Algorithm Log

A chronological record of every layout technique attempted, the math/idea behind each, outcomes, and current status.

Phase 1 — Naive Layout (v0.1.0–v0.1.5)

Approach: Simple vertical stacking within fixed-width stage columns, left-to-right stage ordering, basic cubic Bézier curves for edges.

  • Jobs stacked vertically inside their stage box, in the order they appear in the YAML.
  • Edges drawn as simple cubic Béziers: C (x1+offset, y1), (x2−offset, y2), (x2, y2) with offset = max(|dx|×0.4, 30).
  • Barycenter sort used once to reorder jobs within a stage by the average position of their neighbors. Single-pass, no iteration.
  • Obstacle-avoidance edge router: if a straight path from source to target crossed a node box, the path was deflected. No dummy nodes.

Result: Functional but looked amateur. Lots of edge crossings. Jobs in large stages formed a long vertical column with no relation to their connections.


Phase 2 — Sugiyama Framework (v0.1.6–v0.1.7)

Idea: The Sugiyama method (Sugiyama, Tagawa, Toda 1981) is the standard four-step approach for layered directed graph drawing:

  1. Layer assignment — already given by GitLab CI stages.
  2. Dummy node insertion — for edges spanning multiple layers, insert virtual "dummy" nodes at each intermediate layer so every edge connects adjacent layers only. This lets crossing minimization operate uniformly.
  3. Crossing minimization — 24-sweep median heuristic with adjacent-transpose improvement. Each sweep fixes one layer and reorders the next by sorting nodes by the median position of their neighbors. After sorting, adjacent pairs are swapped if it reduces crossings.
  4. Vertical coordinate assignment — Catmull-Rom splines routed through dummy waypoints to produce smooth S-curves.

Math — Median Heuristic: For a node $v$ in layer $L_i$ with neighbors $N(v)$ in layer $L_{i-1}$, the median position is: $$\text{med}(v) = \text{positions}(N(v))\left[\lfloor |N(v)|/2 \rfloor\right]$$ Nodes are sorted by their median. Ties are broken by current position.

Math — Catmull-Rom Splines: For consecutive points $P_0, P_1, P_2, P_3$, the segment from $P_1$ to $P_2$ uses control points: $$CP_1 = P_1 + \frac{P_2 - P_0}{6}, \quad CP_2 = P_2 - \frac{P_3 - P_1}{6}$$ Producing a C cp1x cp1y, cp2x cp2y, p2x p2y SVG cubic Bézier.

Result: Edge crossings reduced dramatically. Smooth curves. But dummy nodes inflated stage heights massively (each dummy was treated as a full-height job).


Phase 3 — Dummy Node Height Fix (v0.1.8)

Problem: Dummy nodes were given NODE_HEIGHT (56 px) worth of vertical space, making stages with many pass-through edges enormous.

Fix: Introduced DUMMY_GAP = 8 — dummy nodes only consume 8 px of vertical space. Stage height computed from real jobs only.

Result: ✅ Stage sizes back to normal.


Phase 4 — Barycenter Relaxation for Y Coordinates (v0.1.8)

Problem: Median heuristic produced "crooked" lines — edges had unnecessary vertical zigzags because median is discontinuous (jumps between neighbor positions).

Fix: Replaced median with barycenter (mean) pull plus damped relaxation over 12 iterations, with α decaying from 0.8 to 0.44. Added dummy chain straightening: for each chain of dummy nodes, snap them onto the straight line between their real source and target endpoints.

Math — Barycenter: $$\bar{y}(v) = \frac{1}{|N(v)|} \sum_{u \in N(v)} y(u)$$ Then: $y(v) \leftarrow y(v) + \alpha \cdot (\bar{y}(v) - y(v))$, with $\alpha$ decaying each iteration.

Math — Dummy Chain Straightening: For a chain $[s, d_1, d_2, \ldots, d_k, t]$, set each $d_i$ to: $$y(d_i) = y(s) + \frac{i}{k+1} \cdot (y(t) - y(s))$$

Result: ✅ Much straighter edges. Cleaner overall appearance.


Phase 5 — Same-Stage Dependencies (v0.1.8–v0.1.9)

Problem: GitLab CI allows needs: to reference a job in the same stage (e.g., tf_plan_website depends on build_website in the same build stage). These were being drawn as cross-stage edges, breaking the visualization.

Fix:

  1. Topological sort within stages — jobs with intra-stage dependencies are sorted so the depended-upon job comes first.
  2. Same-stage arc routing — same-stage edges drawn as right-side arcs: exit right from source, bulge 40 px to the right, enter right on target. Added sameStage flag to OriginalEdgeChain.

Result: ✅ Intra-stage dependencies render correctly without disrupting cross-stage layout.


Phase 6 — Absolute Job Positioning (v0.1.9)

Problem: Stage boxes were computed first, then jobs placed relative to them. This "trapped" jobs inside predetermined stage geometry, preventing the Sugiyama algorithm from actually moving them to optimal positions.

Fix: Reversed the approach: the algorithm assigns absolute Y positions to jobs. Stage boxes then wrap around wherever the jobs actually landed (using the bounding box of all jobs in the stage, plus padding).

Result: ✅ Jobs now truly positioned by the algorithm; stages are just visual containers.


Phase 7 — Compaction Pass (v0.1.10)

Problem: After relaxation, jobs within a stage were too spread out vertically, leaving large empty gaps.

Fix: Post-layout compaction: re-pack jobs vertically with standard JOB_GAP spacing, centered on the centroid of the algorithm's placement.

Result: ✅ Compact stages, ❌ but this effectively undid the algorithm's Y-positioning, reverting to a near-vertical-stack.


Phase 8 — Multi-Column Grid Layout (v0.1.11) ❌ REJECTED

Problem: Stages with many jobs (10+) produced very tall, narrow columns. User wanted horizontal spreading.

Approach: Column-major grid fill. Constants MAX_JOBS_PER_COL = 5, COL_GAP = 14. Jobs filled top-to-bottom, left-to-right. Stage width adapted: cols = ceil(n / MAX_JOBS_PER_COL), width = cols × NODE_WIDTH + (cols−1) × COL_GAP + padding.

Result: ❌ User rejected. Grid is too rigid — jobs with different connectivity patterns all get the same fixed positions. 3-job stages shouldn't necessarily be a vertical stack either. The user demanded: "search how to position nodes in a graph, don't use a fixed grid".


Phase 9 — Force-Directed Micro-Layout Per Stage (v0.1.12) ✅ CURRENT

Idea: Within each stage, run a mini force-directed simulation so jobs find natural 2D positions based on their connectivity to other stages.

Forces applied per stage (80 iterations, cooling schedule):

  1. Repulsion (all pairs) — Coulomb-like: $$F_{rep} = \frac{K_{rep}}{d^2} \cdot \hat{r}, \quad K_{rep} = 8000$$ Prevents overlap. Direction along the vector between node centers.

  2. Attraction to ideal Y — each job computes an "ideal Y" from the average Y of its connected neighbors in adjacent stages (barycenter pull): $$y_{ideal}(v) = \frac{1}{|N(v)|}\sum_{u \in N(v)} y(u)$$ $$F_{attr,y} = K_{attr} \cdot (y_{ideal} - y_v), \quad K_{attr} = 0.15$$

  3. X centering — gentle pull toward the horizontal center of the stage: $$F_{center,x} = K_c \cdot (x_{center} - x_v), \quad K_c = 0.02$$

  4. Overlap resolution — hard constraint: after each iteration, detect rectangular overlaps (inflated by JOB_GAP) and push apart along the axis of least overlap.

  5. Boundary enforcement — clamp nodes inside stage padding.

Velocity integration with damping: $$v_t = (v_{t-1} + F) \cdot \delta \cdot \alpha, \quad \delta = 0.85, \quad \alpha = 1 - t/T$$

Stage width adapts: cols = ceil(sqrt(n)) to give horizontal room.

Result: ✅ Jobs position freely based on connectivity. Multi-column layouts emerge naturally for large stages; simple vertical stacks for small stages.


Phase 10 — Edge Angle Penalization (v0.1.13) ✅ CURRENT

Problem: Edges were visually "crooked" — steep angles over short horizontal distances, and some curves bending backward (right-to-left).

Three sub-techniques:

10a. X-Monotone Spline Control Points

Clamp Catmull-Rom control points to stay between $P_1.x$ and $P_2.x$: $$cp_{1x} = \text{clamp}(cp_{1x},; P_1.x,; P_2.x)$$ This guarantees the cubic Bézier segment never curves backward.

10b. Segment-Length-Based Tension

Short segments produce sharp bends with full Catmull-Rom tension. Scale tension by segment length: $$\tau = \min\left(1, \frac{|P_2 - P_1|}{200}\right)$$ Then: $CP_1 = P_1 + \tau \cdot \frac{P_2 - P_0}{6}$. Short segments → nearly straight lines.

Also clamp Y overshoot: $|cp_y - p_y| \leq 0.75 \cdot |P_2.y - P_1.y| + 20$.

10c. Global Edge-Angle Refinement (25 iterations)

Post-layout pass that evaluates every cross-stage edge's steepness and applies corrective Y forces to the source and target jobs:

$$\text{ratio} = \frac{\Delta y}{\Delta x}, \quad F = K_{angle} \cdot \text{ratio} \cdot \alpha \cdot m_{backward}$$

where $K_{angle} = 0.06$, $m_{backward} = 3.0$ if $\Delta x < 0$ (backward edge), else $1.0$.

Source is pushed toward target's Y and vice versa. Overlap resolution runs each iteration to prevent nodes from stacking.

Result: ✅ Edges flow more horizontally. No backward-curving splines.


Phase 11 — Edge-Node Avoidance Routing (v0.1.14–v0.1.15) ❌ REJECTED

Problem: Edges sometimes draw directly over/through job boxes that aren't their source or target.

Approach: Cohen–Sutherland segment-rectangle intersection detection + bypass waypoints. Also X-monotonicity clamping of intermediate waypoints.

Bugs encountered (v0.1.14):

  1. Edge key used \0 separator but avoidance code parsed with -> — source/target never identified, every edge tried to bypass its own nodes.
  2. Same-stage arcs entered target on RIGHT side (wrong), creating backward curves.
  3. Bypass X clamping assumed left-to-right direction.

Fixes (v0.1.15): Key parsing fixed, same-stage arcs redesigned as rectangular detours, direction-aware clamping.

Result: ❌ Still produced messy edges. Fundamental problem: dummy-node waypoints + bypass insertion + Catmull-Rom splines = too many systems fighting each other. Bypass waypoints created new crossings, tension scaling distorted curves, X-monotonicity clamping moved waypoints into unexpected positions.


Phase 12 — Port-Based Direct S-Curve Routing (v0.1.16) ✅ CURRENT

Insight: Modern pipeline visualizers (GitLab UI, GitHub Actions, Azure Pipelines) don't route edges through intermediate waypoints at all. They use simple port-to-port Bézier S-curves. The Sugiyama dummy nodes are only needed for crossing minimization ordering — they should NOT appear in the final edge drawing.

Approach — three components:

12a. Port Assignment

Each node's right side (outgoing) and left side (incoming) gets evenly-spaced connection ports. Ports are sorted by the Y position of the connected node, which naturally minimizes edge crossings at connection points.

For a node with $k$ outgoing edges, port $i$ (0-indexed, sorted by target Y) is placed at: $$y_{port}(i) = y_{top} + m + \frac{(h - 2m) \cdot i}{k - 1}$$ where $m = 8$ px margin, $h = $ node height. Single edges connect at center.

12b. Horizontal S-Curve Bézier

Each cross-stage edge is exactly 2 points — source right port and target left port. Rendered as a single cubic Bézier with purely horizontal control points: $$C_1 = (x_1 + \text{offset},; y_1), \quad C_2 = (x_2 - \text{offset},; y_2)$$ $$\text{offset} = \max(|x_2 - x_1| \times 0.4,; 30)$$

This guarantees: left-to-right flow, smooth curves, no intermediate waypoints to go wrong, no backward bends.

12c. Same-Stage Arcs

Kept as 4-point rectangular detours: exit right → horizontal to arc point → vertical → enter left.

Eliminated: All dummy-node waypoints in edges, edge-node avoidance (Cohen–Sutherland), X-monotonicity clamping, tension-scaled Catmull-Rom, Y-overshoot clamping. Dummy nodes still used for crossing minimization ordering.

Result: ✅ Clean, professional-looking edges. Every edge is a simple smooth S-curve from source to target.


Roadmap

  • [ ] GitLab API integration — OAuth login, view recent pipeline runs, check job status, read job logs directly from VS Code
  • [ ] include:remote / include:template support — resolve remote includes for more complete visualization
  • [ ] Job filtering — filter by stage, status, or search by name
  • [ ] Export graph — export pipeline visualization as SVG/PNG
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft