Skip to content

feat: Add migration graph subcommand to generate a graph for visualization - #643

Open
ElijahAhianyo wants to merge 8 commits into
masterfrom
elijah/migration-graph
Open

feat: Add migration graph subcommand to generate a graph for visualization#643
ElijahAhianyo wants to merge 8 commits into
masterfrom
elijah/migration-graph

Conversation

@ElijahAhianyo

@ElijahAhianyo ElijahAhianyo commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Description

Add graph subcommand to the migration subcommand to generate graphs in Graphiz dot format and mermaidjs.

Example usage

> cargo run migration graph
digraph migrations {
  rankdir=LR;
  splines=spline;
  nodesep=0.4;
  ranksep=0.6;
  bgcolor="transparent";

  graph [fontname="Helvetica,Arial,sans-serif"];
  node  [fontname="Helvetica,Arial,sans-serif", fontsize=11];
  edge  [fontname="Helvetica,Arial,sans-serif", fontsize=9];

  node [
    shape=box,
    style="rounded,filled",
    fillcolor="#eef2ff",
    color="#4c51bf",
    fontcolor="#1e1b4b",
    penwidth=1,
    margin="0.18,0.12"
  ];

  edge [
    color="#9aa5b1",
    penwidth=1.2,
    arrowsize=0.8
  ];

  subgraph cluster_0 {
    label="cot";
    style="rounded,filled";
    color="#d1d5db";
    fillcolor="#f9fafb";
    fontcolor="#374151";
    fontsize=12;
    margin=12;
    n0 [label="m_0001_initial"];
  }
  subgraph cluster_1 {
    label="cot_session";
    style="rounded,filled";
    color="#d1d5db";
    fillcolor="#f9fafb";
    fontcolor="#374151";
    fontsize=12;
    margin=12;
    n1 [label="m_0001_initial"];
  }
  subgraph cluster_2 {
    label="customers";
    style="rounded,filled";
    color="#d1d5db";
    fillcolor="#f9fafb";
    fontcolor="#374151";
    fontsize=12;
    margin=12;
    n2 [label="m_0001_initial"];
    n3 [label="m_0002_auto\n20260527_004236"];
  }

  n2 -> n3;
}

By default, this uses the dot format and prints to stdout.
This is analogous to :

> cargo run migration graph --format dot
Screenshot 2026-08-27 at 5 56 49 PM
  1. Mermaid
> cargo run migration graph --format mermaid
%%{init: {'theme': 'base', 'themeVariables': {'background': 'transparent'}}}%%
flowchart LR
  classDef migration fill:#eef2ff,stroke:#4c51bf,stroke-width:1px,color:#1e1b4b,font-size:12px,rx:6,ry:6;

  subgraph cluster0["cot"]
    n0["m_0001_initial"]
  end
  subgraph cluster1["cot_session"]
    n1["m_0001_initial"]
  end
  subgraph cluster2["customers"]
    n2["m_0001_initial"]
    n3["m_0002_auto<br/>20260527_004236"]
  end

  n2 --> n3

  class n0,n1,n2,n3 migration;
  style cluster0 fill:#f9fafb,stroke:#d1d5db,stroke-width:1px
  style cluster1 fill:#f9fafb,stroke:#d1d5db,stroke-width:1px
  style cluster2 fill:#f9fafb,stroke:#d1d5db,stroke-width:1px
  linkStyle default stroke:#9aa5b1,stroke-width:1.5px
Screenshot 2026-08-27 at 5 55 40 PM

You can also specify an output file to write the output to :


> cargo run migration graph --format dot --output migration.dot

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Refactor / cleanup
  • Performance improvement
  • Other (describe above)

@github-actions github-actions Bot added C-lib Crate: cot (main library crate) C-core labels Aug 27, 2026
@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Projectcot
Branchelijah/migration-graph
Testbedgithub-ubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
microseconds (µs)
(Result Δ%)
Upper Boundary
microseconds (µs)
(Limit %)
empty_router/empty_router📈 view plot
🚷 view threshold
13,443.00 µs
(+54.90%)Baseline: 8,678.27 µs
16,573.66 µs
(81.11%)
json_api/json_api📈 view plot
🚷 view threshold
1,016.40 µs
(-3.22%)Baseline: 1,050.26 µs
1,370.15 µs
(74.18%)
nested_routers/nested_routers📈 view plot
🚷 view threshold
961.45 µs
(-2.17%)Baseline: 982.77 µs
1,255.98 µs
(76.55%)
single_root_route/single_root_route📈 view plot
🚷 view threshold
924.80 µs
(-2.28%)Baseline: 946.35 µs
1,218.60 µs
(75.89%)
single_root_route_burst/single_root_route_burst📈 view plot
🚷 view threshold
17,608.00 µs
(+3.02%)Baseline: 17,091.19 µs
21,557.42 µs
(81.68%)
🐰 View full continuous benchmarking report in Bencher

Comment thread cot/src/db/migrations/graph_export.rs Outdated
// canvas regardless of where it's rendered.
let _ = writeln!(
out,
"%%{{init: {{'theme': 'base', 'themeVariables': {{'background': 'transparent'}}}}}}%%"

@ElijahAhianyo ElijahAhianyo Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the background to transparent, on some occasions, doesn't work well with dark mode. It sometimes leaves a white background. check example here. I think the best way to handle this is to set styles for dark mode and light mode themes and also have users specify themes in the CLI but also provide a senseble default. I didn't want to spend time doing that in this PR and its also very low priority since the point is to allow users to visualize the graph, not necessarily to render it on a page.

@ElijahAhianyo
ElijahAhianyo requested a review from a team August 27, 2026 23:18
Comment thread cot/src/db/migrations/graph_export.rs Outdated

@m4tx m4tx left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey! This looks like a nice change, but I think it should be separated from the MigrationEngine to adhere to the responsibility principle.

Comment thread cot/src/db/migrations.rs Outdated
/// # Errors
///
/// Returns an error if the dependency graph cannot be generated
pub fn to_graph(&self, format: GraphFormat) -> Result<String> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite sure if we should expose this as a public API. Public API means it's difficult to change/remove, and I believe it's not a functionality that would be particularly useful outside of the CLI. What's even more important, this functionality lies completely outside of the "MigrationEngine" scope. The role of this struct is to handle applying and rolling back migrations in a safe manner. Rendering a graph is pretty much unrelated to that.

I think we should remove this from MigrationEngine and instead it should just be a separate feature accessible only through the CLI subcommand. We'll probably need to add a getter that will return the migrations slice in the MigrationEngine.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah I completely agree. should've done a better job with the abstraction

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved this into a GraphExporter struct now

Comment thread cot/src/db/migrations/graph_export.rs
@ElijahAhianyo
ElijahAhianyo requested a review from m4tx August 28, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

C-core C-lib Crate: cot (main library crate)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants