| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- #!/usr/bin/env elixir
- defmodule CheckElixirApplications do
- alias EMQXUmbrella.MixProject
- @default_applications [:kernel, :stdlib, :sasl]
- def main() do
- {:ok, _} = Application.ensure_all_started(:mix)
- File.cwd!()
- |> Path.join("mix.exs")
- |> Code.compile_file()
- inputs = MixProject.check_profile!()
- profile = Mix.env()
- # produce `rebar.config.rendered` to consult
- File.cwd!()
- |> Path.join("rebar3")
- |> System.cmd(["as", to_string(profile)],
- env: [{"DEBUG", "1"}]
- )
- mix_apps = mix_applications(inputs.release_type, inputs.edition_type)
- rebar_apps = rebar_applications(profile)
- results = diff_apps(mix_apps, rebar_apps)
- report_discrepancy(
- results[:missing_apps],
- "* There are missing applications in the Elixir release",
- fn %{app: app, mode: mode, after: last_app} ->
- IO.puts(" * #{app}: #{inspect(mode)} should be placed after #{inspect(last_app)}")
- end
- )
- report_discrepancy(
- results[:different_modes],
- "* There are applications with different application modes in the Elixir release",
- fn %{app: app, rebar_mode: rebar_mode, mix_mode: mix_mode} ->
- IO.puts(
- " * #{inspect(app)} should have mode #{inspect(rebar_mode)}, but it has mode #{inspect(mix_mode)}"
- )
- end
- )
- report_discrepancy(
- results[:different_positions],
- "* There are applications in the Elixir release in the wrong order",
- fn %{app: app, mode: mode, after: last_app} ->
- IO.puts(" * #{app}: #{inspect(mode)} should be placed after #{inspect(last_app)}")
- end
- )
- success? =
- results
- |> Map.take([:missing_apps, :different_modes, :different_positions])
- |> Map.values()
- |> Enum.concat()
- |> Enum.empty?()
- if not success? do
- System.halt(1)
- else
- IO.puts(
- IO.ANSI.green() <>
- "Mix and Rebar applications OK!" <>
- IO.ANSI.reset()
- )
- end
- end
- defp mix_applications(release_type, edition_type) do
- EMQXUmbrella.MixProject.applications(release_type, edition_type)
- end
- defp rebar_applications(profile) do
- {:ok, props} =
- File.cwd!()
- |> Path.join("rebar.config.rendered")
- |> :file.consult()
- props[:profiles][profile][:relx]
- |> Enum.find(&(elem(&1, 0) == :release))
- |> elem(2)
- |> Enum.map(fn
- app when is_atom(app) ->
- {app, :permanent}
- {app, mode} ->
- {app, mode}
- end)
- |> Enum.reject(fn {app, _mode} ->
- # Elixir already includes those implicitly
- app in @default_applications
- end)
- end
- defp diff_apps(mix_apps, rebar_apps) do
- app_names = Keyword.keys(rebar_apps)
- mix_apps = Keyword.filter(mix_apps, fn {app, _mode} -> app in app_names end)
- acc = %{
- mix_apps: mix_apps,
- missing_apps: [],
- different_positions: [],
- different_modes: [],
- last_app: nil
- }
- Enum.reduce(
- rebar_apps,
- acc,
- fn
- {rebar_app, rebar_mode}, acc = %{mix_apps: [], last_app: last_app} ->
- missing_app = %{
- app: rebar_app,
- mode: rebar_mode,
- after: last_app
- }
- acc
- |> Map.update!(:missing_apps, &[missing_app | &1])
- |> Map.put(:last_app, rebar_app)
- {rebar_app, rebar_mode},
- acc = %{mix_apps: [{mix_app, mix_mode} | rest], last_app: last_app} ->
- case {rebar_app, rebar_mode} do
- {^mix_app, ^mix_mode} ->
- acc
- |> Map.put(:mix_apps, rest)
- |> Map.put(:last_app, rebar_app)
- {^mix_app, _mode} ->
- different_mode = %{
- app: rebar_app,
- rebar_mode: rebar_mode,
- mix_mode: mix_mode
- }
- acc
- |> Map.put(:mix_apps, rest)
- |> Map.update!(:different_modes, &[different_mode | &1])
- |> Map.put(:last_app, rebar_app)
- {_app, _mode} ->
- case Keyword.pop(rest, rebar_app) do
- {nil, _} ->
- missing_app = %{
- app: rebar_app,
- mode: rebar_mode,
- after: last_app
- }
- acc
- |> Map.update!(:missing_apps, &[missing_app | &1])
- |> Map.put(:last_app, rebar_app)
- {^rebar_mode, rest} ->
- different_position = %{
- app: rebar_app,
- mode: rebar_mode,
- after: last_app
- }
- acc
- |> Map.update!(:different_positions, &[different_position | &1])
- |> Map.put(:last_app, rebar_app)
- |> Map.put(:mix_apps, [{mix_app, mix_mode} | rest])
- {mode, rest} ->
- different_mode = %{
- app: rebar_app,
- rebar_mode: rebar_mode,
- mix_mode: mode
- }
- different_position = %{
- app: rebar_app,
- mode: rebar_mode,
- after: last_app
- }
- acc
- |> Map.put(:mix_apps, [{mix_app, mix_mode} | rest])
- |> Map.update!(:different_modes, &[different_mode | &1])
- |> Map.update!(:different_positions, &[different_position | &1])
- |> Map.put(:last_app, rebar_app)
- end
- end
- end
- )
- end
- defp report_discrepancy(diffs, header, line_fn) do
- unless Enum.empty?(diffs) do
- IO.puts(IO.ANSI.red() <> header)
- diffs
- |> Enum.reverse()
- |> Enum.each(line_fn)
- IO.puts(IO.ANSI.reset())
- end
- end
- end
- CheckElixirApplications.main()
|