Packages

Smart boundary enforcement for Elixir projects

Retired package: Release invalid - Use ~> 0.1.1 instead

Current section

Files

Jump to
frontier lib mix tasks frontier.visualize.ex
Raw

lib/mix/tasks/frontier.visualize.ex

defmodule Mix.Tasks.Frontier.Visualize do
@shortdoc "Generates a visual dependency graph of Frontier boundaries."
@moduledoc """
Generates a DOT or PNG graph of Frontier boundary relationships.
The graph shows actual cross-context calls detected by the compiler tracer,
colored by whether they're allowed or violations:
* **Green edges** — allowed cross-context calls
* **Red edges** — boundary violations
* **Dashed orange edges** — known violations silenced via `skip_violations:`
Violations are listed in a legend at the bottom of the graph.
$ mix frontier.visualize # outputs frontier.dot
$ mix frontier.visualize --format png # outputs frontier.png (requires graphviz)
$ mix frontier.visualize --output ./docs # custom output directory
## Options
* `--format` - Output format: `dot` (default) or `png`
* `--output` - Output directory (default: current directory)
"""
use Mix.Task
alias Frontier.Config
alias Frontier.Graph
alias Frontier.Visualization
@impl Mix.Task
def run(argv) do
{opts, _rest, _errors} =
OptionParser.parse(argv, strict: [format: :string, output: :string])
Mix.Task.run("compile")
Config.init()
format = Keyword.get(opts, :format, "dot")
output_dir = Keyword.get(opts, :output, File.cwd!())
File.mkdir_p!(output_dir)
dot_content =
Visualization.build()
|> Graph.to_dot()
dot_path = Path.join(output_dir, "frontier.dot")
File.write!(dot_path, dot_content)
Mix.shell().info([:green, "Generated #{dot_path}"])
if format == "png" do
png_path = Path.join(output_dir, "frontier.png")
generate_png(dot_path, png_path)
end
end
defp generate_png(dot_path, png_path) do
case System.find_executable("dot") do
nil ->
Mix.shell().info([
:yellow,
"Graphviz `dot` not found. Install it to generate PNG:\n",
" brew install graphviz\n",
"DOT file saved — you can render it manually:\n",
" dot -Tpng #{dot_path} -o #{png_path}"
])
dot_cmd ->
case System.cmd(dot_cmd, ["-Tpng", dot_path, "-o", png_path]) do
{_output, 0} ->
Mix.shell().info([:green, "Generated #{png_path}"])
{output, _code} ->
Mix.shell().info([:red, "Failed to generate PNG: #{output}"])
end
end
end
end