Packages

Multi-surface application runtime for Elixir. One TEA module renders to terminal, browser (LiveView), SSH, and MCP (agents). 30+ widgets, flexbox + CSS grid, AI agent runtime, distributed swarm with CRDTs, time-travel debugging, session recording, sandboxed REPL, and agentic commerce.

Current section

Files

Jump to
raxol lib mix tasks raxol.pre_commit.ex.disabled
Raw

lib/mix/tasks/raxol.pre_commit.ex.disabled

defmodule Mix.Tasks.Raxol.PreCommit do
@moduledoc """
Run all pre-commit checks for the Raxol project.
## Usage
mix raxol.pre_commit [options]
## Options
* `--parallel` - Run checks in parallel (default: true)
* `--fail-fast` - Stop on first failure (default: false)
* `--only` - Run only specific checks (comma-separated)
* `--skip` - Skip specific checks (comma-separated)
* `--quiet` - Minimal output
* `--verbose` - Detailed output with timing and cache info
* `--fix` - Auto-fix issues when possible (currently: format)
* `--no-cache` - Disable cache, run all checks fresh
## Examples
# Run all checks
mix raxol.pre_commit
# Run with auto-fix
mix raxol.pre_commit --fix
# Force fresh check without cache
mix raxol.pre_commit --no-cache
# Run only format and compile checks
mix raxol.pre_commit --only format,compile
# Skip docs check
mix raxol.pre_commit --skip docs
# Sequential execution with fail-fast
mix raxol.pre_commit --no-parallel --fail-fast
# Verbose output with cache statistics
mix raxol.pre_commit --verbose
"""
use Mix.Task
alias Raxol.PreCommit.{Config, Progress, ErrorFormatter, Metrics}
@shortdoc "Run all pre-commit checks"
@check_modules %{
format: Mix.Tasks.Raxol.Check.Format,
compile: Mix.Tasks.Raxol.Check.Compile,
credo: Mix.Tasks.Raxol.Check.Credo,
tests: Mix.Tasks.Raxol.Check.Tests,
docs: Mix.Tasks.Raxol.Check.Docs,
dialyzer: Mix.Tasks.Raxol.Check.Dialyzer,
security: Mix.Tasks.Raxol.Check.Security
}
@impl Mix.Task
def run(args) do
{opts, _, _} =
OptionParser.parse(args,
switches: [
parallel: :boolean,
fail_fast: :boolean,
only: :string,
skip: :string,
quiet: :boolean,
verbose: :boolean,
fix: :boolean,
no_cache: :boolean
],
aliases: [
p: :parallel,
f: :fail_fast,
q: :quiet,
v: :verbose
]
)
# Load configuration from all sources
config = Config.load(build_cli_config(opts))
unless config.quiet do
IO.puts("\nšŸš€ Running Raxol Pre-commit Checks\n")
end
start_time = System.monotonic_time(:millisecond)
# Start progress indicator if enabled
maybe_start_progress(config)
checks = determine_checks(config)
# Initialize all checks in progress display
maybe_init_checks(checks, config)
results = run_checks(checks, config)
# Stop progress indicator
maybe_stop_progress(config)
elapsed = System.monotonic_time(:millisecond) - start_time
# Record metrics unless disabled
unless Map.get(config, :skip_metrics, false) do
Metrics.record_run(results, elapsed, config)
end
print_summary(results, elapsed, config)
exit_code = if all_passed?(results), do: 0, else: 1
System.halt(exit_code)
end
defp build_cli_config(opts) do
# Build config map from CLI options
cli_config = %{}
cli_config = maybe_put(cli_config, :parallel, Keyword.get(opts, :parallel))
cli_config =
maybe_put(cli_config, :fail_fast, Keyword.get(opts, :fail_fast))
cli_config = maybe_put(cli_config, :quiet, Keyword.get(opts, :quiet))
cli_config = maybe_put(cli_config, :verbose, Keyword.get(opts, :verbose))
cli_config = maybe_put(cli_config, :auto_fix, Keyword.get(opts, :fix))
cli_config = maybe_put(cli_config, :no_cache, Keyword.get(opts, :no_cache))
# Handle special cases
cli_config =
case Keyword.get(opts, :only) do
nil -> cli_config
only_str -> Map.put(cli_config, :checks, parse_check_list(only_str))
end
cli_config =
case Keyword.get(opts, :skip) do
nil -> cli_config
skip_str -> Map.put(cli_config, :skip, parse_check_list(skip_str))
end
cli_config
end
defp maybe_put(map, _key, nil), do: map
defp maybe_put(map, key, value), do: Map.put(map, key, value)
defp parse_check_list(nil), do: []
defp parse_check_list(str) do
str
|> String.split(",")
|> Enum.map(&String.trim/1)
|> Enum.map(&String.to_atom/1)
end
defp determine_checks(config) do
# Get checks from config (defaults, file config, or CLI override)
base_checks =
Map.get(config, :checks, [:format, :compile, :credo, :tests, :docs])
# Apply skip filter if present
skip_list = Map.get(config, :skip, [])
checks =
case skip_list do
[] -> base_checks
skips -> Enum.reject(base_checks, &(&1 in skips))
end
checks
|> Enum.map(&{&1, Map.get(@check_modules, &1)})
|> Enum.filter(fn {_, module} -> module != nil end)
end
defp run_checks(checks, %{parallel: true, fail_fast: false} = config) do
# Smart parallel execution with CPU core detection
max_concurrency = determine_concurrency(checks)
checks
|> Task.async_stream(
fn {name, module} ->
maybe_start_check(name, config)
check_config = Config.get_check_config(config, name)
result = run_single_check(module, check_config)
maybe_update_check_status(name, result, config)
{name, result}
end,
ordered: true,
timeout: 30_000,
max_concurrency: max_concurrency
)
|> Enum.map(fn {:ok, result} -> result end)
end
defp run_checks(checks, %{parallel: false} = config) do
Enum.reduce_while(checks, [], fn {name, module}, acc ->
maybe_start_check(name, config)
check_config = Config.get_check_config(config, name)
result = run_single_check(module, check_config)
maybe_update_check_status(name, result, config)
new_acc = [{name, result} | acc]
if config.fail_fast and result.status == :error do
{:halt, Enum.reverse(new_acc)}
else
{:cont, new_acc}
end
end)
|> Enum.reverse()
end
defp run_checks(checks, config) do
# Parallel with fail-fast (more complex, simplified for now)
run_checks(checks, %{config | parallel: false})
end
defp determine_concurrency(checks) do
# Get CPU cores available
cpu_cores = System.schedulers_online()
# Determine check dependencies
# compile must run before tests (tests might need compiled code)
has_compile = Enum.any?(checks, fn {name, _} -> name == :compile end)
has_tests = Enum.any?(checks, fn {name, _} -> name == :tests end)
if has_compile and has_tests do
# If we have dependent checks, limit concurrency
min(2, cpu_cores)
else
# Otherwise use available cores, but cap at check count
min(length(checks), cpu_cores)
end
end
defp run_single_check(module, config) do
start_time = System.monotonic_time(:millisecond)
result =
try do
# Ensure module is compiled and loaded
case Code.ensure_compiled(module) do
{:module, _} ->
if function_exported?(module, :run, 1) do
module.run(config)
else
{:error, "Check module #{inspect(module)} not implemented"}
end
{:error, _} ->
# Try loading all Mix tasks
Mix.Task.load_all()
# Retry
case Code.ensure_compiled(module) do
{:module, _} ->
if function_exported?(module, :run, 1) do
module.run(config)
else
{:error, "Check module #{inspect(module)} not implemented"}
end
{:error, reason} ->
{:error,
"Module #{inspect(module)} not found: #{inspect(reason)}"}
end
end
rescue
e -> {:error, Exception.format(:error, e, __STACKTRACE__)}
catch
:exit, reason -> {:error, "Check exited: #{inspect(reason)}"}
end
elapsed = System.monotonic_time(:millisecond) - start_time
case result do
{:ok, details} ->
%{status: :ok, details: details, elapsed: elapsed}
{:error, reason} ->
%{status: :error, reason: reason, elapsed: elapsed}
{:warning, reason} ->
%{status: :warning, reason: reason, elapsed: elapsed}
end
end
defp all_passed?(results) do
Enum.all?(results, fn {_, result} ->
result.status in [:ok, :warning]
end)
end
defp print_summary(results, total_elapsed, config) do
unless config.quiet do
IO.puts("\n" <> String.duplicate("─", 50))
IO.puts("šŸ“Š Pre-commit Check Summary\n")
Enum.each(results, fn {name, result} ->
icon =
case result.status do
:ok -> "āœ…"
:warning -> "āš ļø "
:error -> "āŒ"
end
time_str =
if config.verbose do
" (#{result.elapsed}ms)"
else
""
end
check_name = name |> to_string() |> String.capitalize()
IO.puts(" #{icon} #{check_name}#{time_str}")
# Show detailed error information for failures
case {result.status, result[:reason], config.verbose} do
{:error, reason, true} when is_map(reason) ->
ErrorFormatter.print_error(name, reason, verbose: true, color: true)
{:error, reason, _} when is_binary(reason) ->
IO.puts(" #{reason}")
{:warning, reason, _} when is_binary(reason) ->
IO.puts(" #{reason}")
_ ->
:ok
end
end)
IO.puts("\n" <> String.duplicate("─", 50))
passed = Enum.count(results, fn {_, r} -> r.status == :ok end)
warnings = Enum.count(results, fn {_, r} -> r.status == :warning end)
failed = Enum.count(results, fn {_, r} -> r.status == :error end)
total = length(results)
IO.puts(
"Total: #{total} checks, #{passed} passed, #{warnings} warnings, #{failed} failed"
)
IO.puts("Time: #{format_time(total_elapsed)}\n")
case all_passed?(results) do
true ->
IO.puts("✨ All checks passed! Ready to commit.")
false ->
IO.puts("āŒ Pre-commit checks failed. Please fix the issues above.")
# Show error summary if not verbose (verbose shows full errors inline)
unless config.verbose do
ErrorFormatter.print_summary(results, color: true)
end
end
end
end
defp format_time(ms) when ms < 1000, do: "#{ms}ms"
defp format_time(ms), do: "#{Float.round(ms / 1000, 1)}s"
# Progress indicator helpers
defp maybe_start_progress(config) do
case Progress.enabled?(config) do
true ->
{:ok, _} =
Progress.start_link(
verbose: Map.get(config, :verbose, false),
parallel: Map.get(config, :parallel, true)
)
false ->
:ok
end
end
defp maybe_stop_progress(config) do
case Progress.enabled?(config) do
true -> Progress.stop()
false -> :ok
end
catch
:exit, _ -> :ok
end
defp maybe_init_checks(checks, config) do
case Progress.enabled?(config) do
true ->
Enum.each(checks, fn {name, _module} ->
Progress.init_check(name)
end)
false ->
:ok
end
end
defp maybe_start_check(name, config) do
case Progress.enabled?(config) do
true -> Progress.start_check(name)
false -> :ok
end
catch
:exit, _ -> :ok
end
defp maybe_update_check_status(name, result, config) do
case Progress.enabled?(config) do
true ->
case result.status do
:ok -> Progress.complete_check(name, result[:elapsed])
:warning -> Progress.warn_check(name, result[:reason])
:error -> Progress.fail_check(name, result[:reason])
end
false ->
:ok
end
catch
:exit, _ -> :ok
end
end