fixing machine boot
This commit is contained in:
parent
757a944ade
commit
ccd8a2f9d7
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQX.MixProject do
|
defmodule EMQX.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -19,6 +20,7 @@ defmodule EMQX.MixProject do
|
||||||
def application do
|
def application do
|
||||||
[
|
[
|
||||||
mod: {:emqx_app, []},
|
mod: {:emqx_app, []},
|
||||||
|
applications: EMQX.Mix.Common.erl_apps(:emqx),
|
||||||
extra_applications: [:logger, :os_mon, :syntax_tools]
|
extra_applications: [:logger, :os_mon, :syntax_tools]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -33,7 +35,7 @@ defmodule EMQX.MixProject do
|
||||||
{:ekka, github: "emqx/ekka", tag: "0.11.1"},
|
{:ekka, github: "emqx/ekka", tag: "0.11.1"},
|
||||||
# {:gen_rpc, github: "emqx/gen_rpc", tag: "2.5.1"},
|
# {:gen_rpc, github: "emqx/gen_rpc", tag: "2.5.1"},
|
||||||
# {:cuttlefish, github: "emqx/cuttlefish", tag: "v4.0.1"},
|
# {:cuttlefish, github: "emqx/cuttlefish", tag: "v4.0.1"},
|
||||||
{:hocon, github: "emqx/hocon", tag: "0.22.0"},
|
{:hocon, github: "emqx/hocon", tag: "0.22.0", runtime: false},
|
||||||
# {:pbkdf2, github: "emqx/erlang-pbkdf2", tag: "2.0.4"},
|
# {:pbkdf2, github: "emqx/erlang-pbkdf2", tag: "2.0.4"},
|
||||||
# {:snabbkaffe, github: "kafka4beam/snabbkaffe", tag: "0.14.0"},
|
# {:snabbkaffe, github: "kafka4beam/snabbkaffe", tag: "0.14.0"},
|
||||||
# {:jiffy, github: "emqx/jiffy", tag: "1.0.5"},
|
# {:jiffy, github: "emqx/jiffy", tag: "1.0.5"},
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
defmodule EMQXAuthz.MixProject do
|
defmodule EMQXAuthz.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
|
@app :emqx_authz
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
app: :emqx_authz,
|
app: @app,
|
||||||
version: "0.1.0",
|
version: "0.1.0",
|
||||||
build_path: "../../_build",
|
build_path: "../../_build",
|
||||||
config_path: "../../config/config.exs",
|
config_path: "../../config/config.exs",
|
||||||
|
@ -17,6 +20,8 @@ defmodule EMQXAuthz.MixProject do
|
||||||
|
|
||||||
def application do
|
def application do
|
||||||
[
|
[
|
||||||
|
mod: EMQX.Mix.Common.from_erl!(@app, :mod),
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(@app, :applications),
|
||||||
extra_applications: [:logger]
|
extra_applications: [:logger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXBridge.MixProject do
|
defmodule EMQXBridge.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -20,6 +21,7 @@ defmodule EMQXBridge.MixProject do
|
||||||
[
|
[
|
||||||
registered: [],
|
registered: [],
|
||||||
mod: {:emqx_bridge_app, []},
|
mod: {:emqx_bridge_app, []},
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(:emqx_bridge, :applications),
|
||||||
extra_applications: [:logger]
|
extra_applications: [:logger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXConf.MixProject do
|
defmodule EMQXConf.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -19,6 +20,8 @@ defmodule EMQXConf.MixProject do
|
||||||
def application do
|
def application do
|
||||||
[
|
[
|
||||||
mod: {:emqx_conf_app, []},
|
mod: {:emqx_conf_app, []},
|
||||||
|
# applications: EMQX.Mix.Common.erl_apps(:emqx_conf),
|
||||||
|
# included_applications: [:hocon],
|
||||||
# extra_applications: [:logger, :os_mon, :syntax_tools]
|
# extra_applications: [:logger, :os_mon, :syntax_tools]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -26,7 +29,7 @@ defmodule EMQXConf.MixProject do
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:emqx, in_umbrella: true, runtime: false},
|
{:emqx, in_umbrella: true, runtime: false},
|
||||||
# {:hocon, github: "emqx/hocon"}
|
{:hocon, github: "emqx/hocon", tag: "0.22.0"},
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXConnector.MixProject do
|
defmodule EMQXConnector.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -18,6 +19,7 @@ defmodule EMQXConnector.MixProject do
|
||||||
def application do
|
def application do
|
||||||
[
|
[
|
||||||
mod: {:emqx_connector_app, []},
|
mod: {:emqx_connector_app, []},
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(:emqx_connector, :applications),
|
||||||
extra_applications: [:logger]
|
extra_applications: [:logger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -28,6 +30,8 @@ defmodule EMQXConnector.MixProject do
|
||||||
{:epgsql, github: "epgsql/epgsql", tag: "4.4.0"},
|
{:epgsql, github: "epgsql/epgsql", tag: "4.4.0"},
|
||||||
{:mysql, github: "emqx/mysql-otp", tag: "1.7.1"},
|
{:mysql, github: "emqx/mysql-otp", tag: "1.7.1"},
|
||||||
{:emqtt, github: "emqx/emqtt", tag: "1.4.3"},
|
{:emqtt, github: "emqx/emqtt", tag: "1.4.3"},
|
||||||
|
{:eredis_cluster, github: "emqx/eredis_cluster", tag: "0.6.7"},
|
||||||
|
{:mongodb, github: "emqx/mongodb-erlang", tag: "v3.0.10"},
|
||||||
# {:ecpool, github: "emqx/ecpool", tag: "0.5.1"},
|
# {:ecpool, github: "emqx/ecpool", tag: "0.5.1"},
|
||||||
# {:emqtt, github: "emqx/emqtt", tag: "1.4.3"}
|
# {:emqtt, github: "emqx/emqtt", tag: "1.4.3"}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXDashboard.MixProject do
|
defmodule EMQXDashboard.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -20,6 +21,7 @@ defmodule EMQXDashboard.MixProject do
|
||||||
[
|
[
|
||||||
registered: [:emqx_dashboard_sup],
|
registered: [:emqx_dashboard_sup],
|
||||||
mod: {:emqx_dashboard_app, []},
|
mod: {:emqx_dashboard_app, []},
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(:emqx_dashboard, :applications),
|
||||||
extra_applications: [:logger]
|
extra_applications: [:logger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,7 +28,7 @@ defmodule EMQXExhook.MixProject do
|
||||||
|
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:emqx, in_umbrella: true, runtime: false},
|
{:emqx, in_umbrella: true},
|
||||||
{:grpc, github: "emqx/grpc-erl", tag: "0.6.2"},
|
{:grpc, github: "emqx/grpc-erl", tag: "0.6.2"},
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXGateway.MixProject do
|
defmodule EMQXGateway.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -22,6 +23,7 @@ defmodule EMQXGateway.MixProject do
|
||||||
[
|
[
|
||||||
registered: [],
|
registered: [],
|
||||||
mod: {:emqx_gateway_app, []},
|
mod: {:emqx_gateway_app, []},
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(:emqx_gateway, :applications),
|
||||||
extra_applications: [:logger]
|
extra_applications: [:logger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -110,14 +110,37 @@ reboot_apps() ->
|
||||||
|
|
||||||
sorted_reboot_apps() ->
|
sorted_reboot_apps() ->
|
||||||
Apps = [{App, app_deps(App)} || App <- reboot_apps()],
|
Apps = [{App, app_deps(App)} || App <- reboot_apps()],
|
||||||
|
?SLOG(warning, #{ msg => "sorted_reboot_apps/0 before /1"
|
||||||
|
, apps => Apps
|
||||||
|
}),
|
||||||
Res = sorted_reboot_apps(Apps),
|
Res = sorted_reboot_apps(Apps),
|
||||||
io:format(user, "~n>>>>>>>>>>>>>> sorted_reboot_apps~n ~100p~n", [Res]),
|
io:format(user, "~n>>>>>>>>>>>>>> sorted_reboot_apps~n ~100p~n", [Res]),
|
||||||
Res.
|
Res,
|
||||||
|
%% FIXME!!!!! For some reason, emqx_conf appears in the
|
||||||
|
%% `applications` key in resource, but it is not there in the
|
||||||
|
%% .app.src...
|
||||||
|
Res0 = [emqx_conf,gproc,esockd,ranch,cowboy,emqx,emqx_prometheus,emqx_modules,emqx_dashboard,
|
||||||
|
emqx_gateway,emqx_management,emqx_retainer,emqx_statsd,emqx_resource,emqx_connector,emqx_bridge,
|
||||||
|
emqx_authn,emqx_authz,emqx_exhook],
|
||||||
|
case true of
|
||||||
|
true ->
|
||||||
|
Res;
|
||||||
|
false ->
|
||||||
|
Res0
|
||||||
|
end.
|
||||||
|
|
||||||
app_deps(App) ->
|
app_deps(App) ->
|
||||||
case application:get_key(App, applications) of
|
case application:get_key(App, applications) of
|
||||||
undefined -> [];
|
undefined -> [];
|
||||||
{ok, List} -> lists:filter(fun(A) -> lists:member(A, reboot_apps()) end, List)
|
{ok, List} ->
|
||||||
|
?SLOG(warning, #{ msg => ">>>>>> machine_boot:add_deps"
|
||||||
|
, app => App
|
||||||
|
, apps => List
|
||||||
|
, included => application:get_key(App, included_applications)
|
||||||
|
}),
|
||||||
|
lists:filter(fun(A) ->
|
||||||
|
lists:member(A, reboot_apps())
|
||||||
|
end, List)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
sorted_reboot_apps(Apps) ->
|
sorted_reboot_apps(Apps) ->
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXManagement.MixProject do
|
defmodule EMQXManagement.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -20,6 +21,7 @@ defmodule EMQXManagement.MixProject do
|
||||||
[
|
[
|
||||||
registered: [:emqx_management_sup],
|
registered: [:emqx_management_sup],
|
||||||
mod: {:emqx_mgmt_app, []},
|
mod: {:emqx_mgmt_app, []},
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(:emqx_management, :applications),
|
||||||
extra_applications: [:logger, :syntax_tools]
|
extra_applications: [:logger, :syntax_tools]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXModules.MixProject do
|
defmodule EMQXModules.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -19,6 +20,7 @@ defmodule EMQXModules.MixProject do
|
||||||
[
|
[
|
||||||
registered: [:emqx_mod_sup],
|
registered: [:emqx_mod_sup],
|
||||||
mod: {:emqx_modules_app, []},
|
mod: {:emqx_modules_app, []},
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(:emqx_modules, :applications),
|
||||||
extra_applications: [:logger]
|
extra_applications: [:logger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@ defmodule EMQXPrometheus.MixProject do
|
||||||
deps_path: "../../deps",
|
deps_path: "../../deps",
|
||||||
lockfile: "../../mix.lock",
|
lockfile: "../../mix.lock",
|
||||||
elixir: "~> 1.12",
|
elixir: "~> 1.12",
|
||||||
start_permanent: Mix.env() == :prod,
|
# start_permanent: Mix.env() == :prod,
|
||||||
deps: deps(),
|
deps: deps(),
|
||||||
description: "Prometheus for EMQ X"
|
description: "Prometheus for EMQ X"
|
||||||
]
|
]
|
||||||
|
@ -26,7 +26,7 @@ defmodule EMQXPrometheus.MixProject do
|
||||||
|
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:emqx, in_umbrella: true, runtime: false},
|
{:emqx, in_umbrella: true},
|
||||||
{:prometheus, github: "emqx/prometheus.erl", tag: "v3.1.1"}
|
{:prometheus, github: "emqx/prometheus.erl", tag: "v3.1.1"}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXResource.MixProject do
|
defmodule EMQXResource.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -11,7 +12,7 @@ defmodule EMQXResource.MixProject do
|
||||||
lockfile: "../../mix.lock",
|
lockfile: "../../mix.lock",
|
||||||
elixir: "~> 1.12",
|
elixir: "~> 1.12",
|
||||||
# start_permanent: Mix.env() == :prod,
|
# start_permanent: Mix.env() == :prod,
|
||||||
start_permanent: false,
|
# start_permanent: false,
|
||||||
deps: deps()
|
deps: deps()
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
@ -19,7 +20,8 @@ defmodule EMQXResource.MixProject do
|
||||||
def application do
|
def application do
|
||||||
[
|
[
|
||||||
mod: {:emqx_resource_app, []},
|
mod: {:emqx_resource_app, []},
|
||||||
extra_applications: [:logger, :syntax_tools]
|
applications: EMQX.Mix.Common.erl_apps(:emqx_resource),
|
||||||
|
# extra_applications: [:emqx, :emqx_conf],
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,7 +30,8 @@ defmodule EMQXResource.MixProject do
|
||||||
# {:jsx, "3.1.0"},
|
# {:jsx, "3.1.0"},
|
||||||
# {:gproc, "0.9.0"},
|
# {:gproc, "0.9.0"},
|
||||||
{:hocon, github: "emqx/hocon", tag: "0.22.0", runtime: false},
|
{:hocon, github: "emqx/hocon", tag: "0.22.0", runtime: false},
|
||||||
{:emqx, in_umbrella: true, runtime: false}
|
{:emqx, in_umbrella: true},
|
||||||
|
{:emqx_conf, in_umbrella: true},
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,9 +8,10 @@
|
||||||
[kernel,
|
[kernel,
|
||||||
stdlib,
|
stdlib,
|
||||||
gproc,
|
gproc,
|
||||||
hocon,
|
%% hocon,
|
||||||
jsx,
|
jsx,
|
||||||
emqx
|
emqx,
|
||||||
|
emqx_conf
|
||||||
]},
|
]},
|
||||||
{env,[]},
|
{env,[]},
|
||||||
{modules, []},
|
{modules, []},
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXRetainer.MixProject do
|
defmodule EMQXRetainer.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -10,23 +11,24 @@ defmodule EMQXRetainer.MixProject do
|
||||||
deps_path: "../../deps",
|
deps_path: "../../deps",
|
||||||
lockfile: "../../mix.lock",
|
lockfile: "../../mix.lock",
|
||||||
elixir: "~> 1.12",
|
elixir: "~> 1.12",
|
||||||
start_permanent: Mix.env() == :prod,
|
# start_permanent: Mix.env() == :prod,
|
||||||
deps: deps(),
|
deps: deps(),
|
||||||
description: "EMQ X Retainer"
|
description: "EMQ X Retainer"
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
# def application do
|
def application do
|
||||||
# [
|
[
|
||||||
# registered: [:emqx_retainer_sup],
|
registered: [:emqx_retainer_sup],
|
||||||
# mod: {:emqx_retainer_app, []},
|
mod: {:emqx_retainer_app, []},
|
||||||
# # extra_applications: [:logger]
|
applications: EMQX.Mix.Common.erl_apps(:emqx_retainer),
|
||||||
# ]
|
# extra_applications: [:emqx],
|
||||||
# end
|
]
|
||||||
|
end
|
||||||
|
|
||||||
defp deps do
|
defp deps do
|
||||||
[
|
[
|
||||||
{:emqx, in_umbrella: true, runtime: false}
|
{:emqx, in_umbrella: true}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule EMQXStatsd.MixProject do
|
defmodule EMQXStatsd.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
Code.require_file("../../lib/emqx/mix/common.ex")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
|
@ -19,6 +20,7 @@ defmodule EMQXStatsd.MixProject do
|
||||||
def application do
|
def application do
|
||||||
[
|
[
|
||||||
mod: {:emqx_statsd_app, []},
|
mod: {:emqx_statsd_app, []},
|
||||||
|
applications: EMQX.Mix.Common.from_erl!(:emqx_statsd, :applications),
|
||||||
extra_applications: [:logger]
|
extra_applications: [:logger]
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule EMQX.Mix.Common do
|
||||||
|
def erl_apps(app) do
|
||||||
|
from_erl!(app, :applications)
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_erl!(app, key) do
|
||||||
|
path = Path.join("src", "#{app}.app.src")
|
||||||
|
{:ok, [{:application, ^app, props}]} = :file.consult(path)
|
||||||
|
Keyword.fetch!(props, key)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule EMQX.Mix.Helpers do
|
||||||
|
def read_rebar_config!(filepath) do
|
||||||
|
{:ok, config} = read_rebar_config(filepath)
|
||||||
|
config
|
||||||
|
end
|
||||||
|
|
||||||
|
def read_rebar_config(filepath) do
|
||||||
|
filepath
|
||||||
|
|> to_charlist()
|
||||||
|
|> :file.consult()
|
||||||
|
end
|
||||||
|
|
||||||
|
def rebar_to_mix_dep({name, {:git, url, {:tag, tag}}}),
|
||||||
|
do: {name, git: to_string(url), tag: to_string(tag)}
|
||||||
|
|
||||||
|
def rebar_to_mix_dep({name, {:git, url, {:ref, ref}}}),
|
||||||
|
do: {name, git: to_string(url), ref: to_string(ref)}
|
||||||
|
|
||||||
|
def rebar_to_mix_dep({name, {:git, url, {:branch, branch}}}),
|
||||||
|
do: {name, git: to_string(url), branch: to_string(branch)}
|
||||||
|
|
||||||
|
def rebar_to_mix_dep({name, vsn}) when is_list(vsn),
|
||||||
|
do: {name, to_string(vsn)}
|
||||||
|
end
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Mix.Tasks.Compile.RenderConfig do
|
||||||
|
use Mix.Task.Compiler
|
||||||
|
|
||||||
|
def run(_args) do
|
||||||
|
cmd = "./rebar3"
|
||||||
|
args = ["deps"]
|
||||||
|
System.cmd(cmd, args, env: [{"DEBUG", "1"}], into: IO.stream(:stdio, :line))
|
||||||
|
end
|
||||||
|
end
|
17
mix.exs
17
mix.exs
|
@ -1,6 +1,8 @@
|
||||||
defmodule EMQXUmbrella.MixProject do
|
defmodule EMQXUmbrella.MixProject do
|
||||||
use Mix.Project
|
use Mix.Project
|
||||||
|
|
||||||
|
Code.load_file("./mix_release.exs")
|
||||||
|
|
||||||
def project do
|
def project do
|
||||||
[
|
[
|
||||||
apps_path: "apps",
|
apps_path: "apps",
|
||||||
|
@ -107,6 +109,7 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
compiler: :permanent,
|
compiler: :permanent,
|
||||||
runtime_tools: :permanent,
|
runtime_tools: :permanent,
|
||||||
# emqx_conf: :permanent,
|
# emqx_conf: :permanent,
|
||||||
|
# hocon: :load,
|
||||||
emqx: :load,
|
emqx: :load,
|
||||||
emqx_conf: :load, # as per rebar.config.erl
|
emqx_conf: :load, # as per rebar.config.erl
|
||||||
emqx_machine: :permanent,
|
emqx_machine: :permanent,
|
||||||
|
@ -119,7 +122,7 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
emqx_connector: :permanent,
|
emqx_connector: :permanent,
|
||||||
emqx_authn: :permanent,
|
emqx_authn: :permanent,
|
||||||
emqx_authz: :permanent,
|
emqx_authz: :permanent,
|
||||||
# emqx_auto_subscribe: :permanent,
|
emqx_auto_subscribe: :permanent,
|
||||||
emqx_gateway: :permanent,
|
emqx_gateway: :permanent,
|
||||||
emqx_exhook: :permanent,
|
emqx_exhook: :permanent,
|
||||||
emqx_bridge: :permanent,
|
emqx_bridge: :permanent,
|
||||||
|
@ -128,13 +131,13 @@ defmodule EMQXUmbrella.MixProject do
|
||||||
emqx_management: :permanent,
|
emqx_management: :permanent,
|
||||||
emqx_dashboard: :permanent,
|
emqx_dashboard: :permanent,
|
||||||
emqx_statsd: :permanent,
|
emqx_statsd: :permanent,
|
||||||
# emqx_retainer: :permanent,
|
emqx_retainer: :permanent,
|
||||||
emqx_retainer: :none,
|
# emqx_retainer: :none,
|
||||||
emqx_prometheus: :permanent,
|
emqx_prometheus: :permanent,
|
||||||
# emqx_psk: :permanent,
|
emqx_psk: :permanent,
|
||||||
emqx_psk: :none,
|
# emqx_psk: :none,
|
||||||
# emqx_limiter: :permanent,
|
emqx_limiter: :permanent,
|
||||||
emqx_limiter: :none,
|
# emqx_limiter: :none,
|
||||||
observer: :load,
|
observer: :load,
|
||||||
# emqx_mix: :none,
|
# emqx_mix: :none,
|
||||||
],
|
],
|
||||||
|
|
12
mix.lock
12
mix.lock
|
@ -5,6 +5,8 @@
|
||||||
"cowboy_swagger": {:git, "https://github.com/inaka/cowboy_swagger", "bc441df7988da0f5c5d11ae0861c394dc30995c5", [tag: "2.5.0"]},
|
"cowboy_swagger": {:git, "https://github.com/inaka/cowboy_swagger", "bc441df7988da0f5c5d11ae0861c394dc30995c5", [tag: "2.5.0"]},
|
||||||
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
|
"cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"},
|
||||||
"cuttlefish": {:git, "https://github.com/emqx/cuttlefish.git", "6c346563e89ebbd95dbc1c29017adaf9abf85ca1", []},
|
"cuttlefish": {:git, "https://github.com/emqx/cuttlefish.git", "6c346563e89ebbd95dbc1c29017adaf9abf85ca1", []},
|
||||||
|
"earmark": {:hex, :earmark, "1.4.19", "3854a17305c880cc46305af15fb1630568d23a709aba21aaa996ced082fc29d7", [:mix], [{:earmark_parser, ">= 1.4.18", [hex: :earmark_parser, repo: "hexpm", optional: false]}], "hexpm", "d5a8c9f9e37159a8fdd3ea8437fb4e229eaf56d5129b9a011dc4780a4872079d"},
|
||||||
|
"earmark_parser": {:hex, :earmark_parser, "1.4.18", "e1b2be73eb08a49fb032a0208bf647380682374a725dfb5b9e510def8397f6f2", [:mix], [], "hexpm", "114a0e85ec3cf9e04b811009e73c206394ffecfcc313e0b346de0d557774ee97"},
|
||||||
"ecpool": {:git, "https://github.com/emqx/ecpool.git", "0516d2cebd14654ef8c583c347e4a0b01363b86d", [tag: "0.5.1"]},
|
"ecpool": {:git, "https://github.com/emqx/ecpool.git", "0516d2cebd14654ef8c583c347e4a0b01363b86d", [tag: "0.5.1"]},
|
||||||
"eetcd": {:hex, :eetcd, "0.3.4", "27e8b4775230c53a9ef602f62a1603591302b40b2eb195d567edffb35b6cf1a2", [:rebar3], [{:gun, "1.3.3", [hex: :gun, repo: "hexpm", optional: false]}], "hexpm", "b763c0e1a9741d39a62f5a19186a342863eacbc769151c4e81db5790efecefca"},
|
"eetcd": {:hex, :eetcd, "0.3.4", "27e8b4775230c53a9ef602f62a1603591302b40b2eb195d567edffb35b6cf1a2", [:rebar3], [{:gun, "1.3.3", [hex: :gun, repo: "hexpm", optional: false]}], "hexpm", "b763c0e1a9741d39a62f5a19186a342863eacbc769151c4e81db5790efecefca"},
|
||||||
"ehttpc": {:git, "https://github.com/emqx/ehttpc.git", "7b1a76b2353b385725e62f948cd399c7040467f8", [tag: "0.1.12"]},
|
"ehttpc": {:git, "https://github.com/emqx/ehttpc.git", "7b1a76b2353b385725e62f948cd399c7040467f8", [tag: "0.1.12"]},
|
||||||
|
@ -13,11 +15,12 @@
|
||||||
"emqtt": {:git, "https://github.com/emqx/emqtt.git", "25892ef48a979a9dfbd74d86133cb28cf11f3cf4", [tag: "1.4.3"]},
|
"emqtt": {:git, "https://github.com/emqx/emqtt.git", "25892ef48a979a9dfbd74d86133cb28cf11f3cf4", [tag: "1.4.3"]},
|
||||||
"emqx_http_lib": {:git, "https://github.com/emqx/emqx_http_lib.git", "9a1aafcbad1bb35392ebabc0cf102c7bce660432", [tag: "0.4.0"]},
|
"emqx_http_lib": {:git, "https://github.com/emqx/emqx_http_lib.git", "9a1aafcbad1bb35392ebabc0cf102c7bce660432", [tag: "0.4.0"]},
|
||||||
"epgsql": {:git, "https://github.com/epgsql/epgsql.git", "895c8f9d53f08d09ec6a0301c56d3d6f270929f2", [tag: "4.4.0"]},
|
"epgsql": {:git, "https://github.com/epgsql/epgsql.git", "895c8f9d53f08d09ec6a0301c56d3d6f270929f2", [tag: "4.4.0"]},
|
||||||
"eredis": {:git, "https://github.com/emqx/eredis", "75f2b8eedbe631136326680225efbcd2684e93e7", [tag: "1.2.5"]},
|
"eredis": {:hex, :eredis, "1.2.0", "0b8e9cfc2c00fa1374cd107ea63b49be08d933df2cf175e6a89b73dd9c380de4", [:rebar3], [], "hexpm", "d9b5abef2c2c8aba8f32aa018203e0b3dc8b1157773b254ab1d4c2002317f1e1"},
|
||||||
"eredis_cluster": {:git, "https://github.com/emqx/eredis_cluster", "624749b4aef25668e9c7a545427fdc663a04faef", [tag: "0.6.7"]},
|
"eredis_cluster": {:git, "https://github.com/emqx/eredis_cluster.git", "624749b4aef25668e9c7a545427fdc663a04faef", [tag: "0.6.7"]},
|
||||||
"esasl": {:git, "https://github.com/emqx/esasl.git", "96d7ac9f6c156017dd35b30df2dd722ae469c7f0", [tag: "0.2.0"]},
|
"esasl": {:git, "https://github.com/emqx/esasl.git", "96d7ac9f6c156017dd35b30df2dd722ae469c7f0", [tag: "0.2.0"]},
|
||||||
"esockd": {:git, "https://github.com/emqx/esockd.git", "9b959fc11a1c398a589892f335235be6c5b4a454", [tag: "5.8.0"]},
|
"esockd": {:git, "https://github.com/emqx/esockd.git", "9b959fc11a1c398a589892f335235be6c5b4a454", [tag: "5.8.0"]},
|
||||||
"estatsd": {:git, "https://github.com/emqx/estatsd.git", "5184d846b7ecb83509bd4d32695c60428c0198cd", [tag: "0.1.0"]},
|
"estatsd": {:git, "https://github.com/emqx/estatsd.git", "5184d846b7ecb83509bd4d32695c60428c0198cd", [tag: "0.1.0"]},
|
||||||
|
"ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0e11d67e662142fc3945b0ee410c73c8c956717fbeae4ad954b418747c734973"},
|
||||||
"gen_coap": {:git, "https://github.com/emqx/gen_coap.git", "9bf5e7f795badf68e2fb4eb226f576308f5b1bb4", [tag: "v0.3.2"]},
|
"gen_coap": {:git, "https://github.com/emqx/gen_coap.git", "9bf5e7f795badf68e2fb4eb226f576308f5b1bb4", [tag: "v0.3.2"]},
|
||||||
"gen_rpc": {:git, "https://github.com/emqx/gen_rpc.git", "fb7418dc8cf7e97d153fba073bee0fac07dce753", [tag: "2.5.1"]},
|
"gen_rpc": {:git, "https://github.com/emqx/gen_rpc.git", "fb7418dc8cf7e97d153fba073bee0fac07dce753", [tag: "2.5.1"]},
|
||||||
"getopt": {:git, "https://github.com/emqx/getopt.git", "215f2083408e1fe562d441aea6062bf5d9e1fb67", [tag: "v1.0.2"]},
|
"getopt": {:git, "https://github.com/emqx/getopt.git", "215f2083408e1fe562d441aea6062bf5d9e1fb67", [tag: "v1.0.2"]},
|
||||||
|
@ -30,11 +33,14 @@
|
||||||
"jose": {:hex, :jose, "1.11.2", "f4c018ccf4fdce22c71e44d471f15f723cb3efab5d909ab2ba202b5bf35557b3", [:mix, :rebar3], [], "hexpm", "98143fbc48d55f3a18daba82d34fe48959d44538e9697c08f34200fa5f0947d2"},
|
"jose": {:hex, :jose, "1.11.2", "f4c018ccf4fdce22c71e44d471f15f723cb3efab5d909ab2ba202b5bf35557b3", [:mix, :rebar3], [], "hexpm", "98143fbc48d55f3a18daba82d34fe48959d44538e9697c08f34200fa5f0947d2"},
|
||||||
"jsx": {:hex, :jsx, "3.1.0", "d12516baa0bb23a59bb35dccaf02a1bd08243fcbb9efe24f2d9d056ccff71268", [:rebar3], [], "hexpm", "0c5cc8fdc11b53cc25cf65ac6705ad39e54ecc56d1c22e4adb8f5a53fb9427f3"},
|
"jsx": {:hex, :jsx, "3.1.0", "d12516baa0bb23a59bb35dccaf02a1bd08243fcbb9efe24f2d9d056ccff71268", [:rebar3], [], "hexpm", "0c5cc8fdc11b53cc25cf65ac6705ad39e54ecc56d1c22e4adb8f5a53fb9427f3"},
|
||||||
"lc": {:git, "https://github.com/qzhuyan/lc.git", "6f98d098e5aaf4fcd6afbbb2acca96855c474600", [tag: "0.1.2"]},
|
"lc": {:git, "https://github.com/qzhuyan/lc.git", "6f98d098e5aaf4fcd6afbbb2acca96855c474600", [tag: "0.1.2"]},
|
||||||
|
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||||
|
"makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"},
|
||||||
"minirest": {:git, "https://github.com/emqx/minirest.git", "f3f80b3e07295d8b6db22ed456318e0cc9dd167f", [tag: "1.2.7"]},
|
"minirest": {:git, "https://github.com/emqx/minirest.git", "f3f80b3e07295d8b6db22ed456318e0cc9dd167f", [tag: "1.2.7"]},
|
||||||
"mnesia_rocksdb": {:git, "https://github.com/k32/mnesia_rocksdb", "42715650a3b84fe13d23a1e7f65815ff36386b0f", [tag: "0.1.3-k32"]},
|
"mnesia_rocksdb": {:git, "https://github.com/k32/mnesia_rocksdb", "42715650a3b84fe13d23a1e7f65815ff36386b0f", [tag: "0.1.3-k32"]},
|
||||||
"mongodb": {:git, "https://github.com/emqx/mongodb-erlang", "2ffe62f42dafb98eaafead9d340a674c5f9279a5", [tag: "v3.0.10"]},
|
"mongodb": {:git, "https://github.com/emqx/mongodb-erlang.git", "2ffe62f42dafb98eaafead9d340a674c5f9279a5", [tag: "v3.0.10"]},
|
||||||
"mria": {:git, "https://github.com/emqx/mria.git", "0887b3fa2b3576175ac38d32e35177635f01621a", [tag: "0.1.4"]},
|
"mria": {:git, "https://github.com/emqx/mria.git", "0887b3fa2b3576175ac38d32e35177635f01621a", [tag: "0.1.4"]},
|
||||||
"mysql": {:git, "https://github.com/emqx/mysql-otp.git", "bdabac44cc8836a9e23897b7e1b77c7df7e04f70", [tag: "1.7.1"]},
|
"mysql": {:git, "https://github.com/emqx/mysql-otp.git", "bdabac44cc8836a9e23897b7e1b77c7df7e04f70", [tag: "1.7.1"]},
|
||||||
|
"nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"},
|
||||||
"pbkdf2": {:git, "https://github.com/emqx/erlang-pbkdf2.git", "45d9981209ea07a83a58cf85aaf8236457da4342", [tag: "2.0.4"]},
|
"pbkdf2": {:git, "https://github.com/emqx/erlang-pbkdf2.git", "45d9981209ea07a83a58cf85aaf8236457da4342", [tag: "2.0.4"]},
|
||||||
"poolboy": {:git, "https://github.com/emqx/poolboy.git", "29be47db8c2be38b18c908e43a80ebb7b9b6116b", [tag: "1.5.2"]},
|
"poolboy": {:git, "https://github.com/emqx/poolboy.git", "29be47db8c2be38b18c908e43a80ebb7b9b6116b", [tag: "1.5.2"]},
|
||||||
"prometheus": {:git, "https://github.com/emqx/prometheus.erl.git", "a41488df09472448057d264ef520cf2f71d925f8", [tag: "v3.1.1"]},
|
"prometheus": {:git, "https://github.com/emqx/prometheus.erl.git", "a41488df09472448057d264ef520cf2f71d925f8", [tag: "v3.1.1"]},
|
||||||
|
|
|
@ -0,0 +1,874 @@
|
||||||
|
defmodule Mix.Release do
|
||||||
|
IO.puts(
|
||||||
|
IO.ANSI.red()
|
||||||
|
<> "I live!!"
|
||||||
|
<> IO.ANSI.reset()
|
||||||
|
)
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
Defines the release structure and convenience for assembling releases.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
The Mix.Release struct has the following read-only fields:
|
||||||
|
|
||||||
|
* `:name` - the name of the release as an atom
|
||||||
|
* `:version` - the version of the release as a string or
|
||||||
|
`{:from_app, app_name}`
|
||||||
|
* `:path` - the path to the release root
|
||||||
|
* `:version_path` - the path to the release version inside the release
|
||||||
|
* `:applications` - a map of application with their definitions
|
||||||
|
* `:erts_source` - the ERTS source as a charlist (or nil)
|
||||||
|
* `:erts_version` - the ERTS version as a charlist
|
||||||
|
|
||||||
|
The following fields may be modified as long as they keep their defined types:
|
||||||
|
|
||||||
|
* `:boot_scripts` - a map of boot scripts with the boot script name
|
||||||
|
as key and a keyword list with **all** applications that are part of
|
||||||
|
it and their modes as value
|
||||||
|
* `:config_providers` - a list of `{config_provider, term}` tuples where the
|
||||||
|
first element is a module that implements the `Config.Provider` behaviour
|
||||||
|
and `term` is the value given to it on `c:Config.Provider.init/1`
|
||||||
|
* `:options` - a keyword list with all other user supplied release options
|
||||||
|
* `:overlays` - a list of extra files added to the release. If you have a custom
|
||||||
|
step adding extra files to a release, you can add these files to the `:overlays`
|
||||||
|
field so they are also considered on further commands, such as tar/zip. Each entry
|
||||||
|
in overlays is the relative path to the release root of each file
|
||||||
|
* `:steps` - a list of functions that receive the release and returns a release.
|
||||||
|
Must also contain the atom `:assemble` which is the internal assembling step.
|
||||||
|
May also contain the atom `:tar` to create a tarball of the release.
|
||||||
|
|
||||||
|
"""
|
||||||
|
defstruct [
|
||||||
|
:name,
|
||||||
|
:version,
|
||||||
|
:path,
|
||||||
|
:version_path,
|
||||||
|
:applications,
|
||||||
|
:boot_scripts,
|
||||||
|
:erts_source,
|
||||||
|
:erts_version,
|
||||||
|
:config_providers,
|
||||||
|
:options,
|
||||||
|
:overlays,
|
||||||
|
:steps
|
||||||
|
]
|
||||||
|
|
||||||
|
@type mode :: :permanent | :transient | :temporary | :load | :none
|
||||||
|
@type application :: atom()
|
||||||
|
@type t :: %__MODULE__{
|
||||||
|
name: atom(),
|
||||||
|
version: String.t(),
|
||||||
|
path: String.t(),
|
||||||
|
version_path: String.t() | {:from_app, application()},
|
||||||
|
applications: %{application() => keyword()},
|
||||||
|
boot_scripts: %{atom() => [{application(), mode()}]},
|
||||||
|
erts_version: charlist(),
|
||||||
|
erts_source: charlist() | nil,
|
||||||
|
config_providers: [{module, term}],
|
||||||
|
options: keyword(),
|
||||||
|
overlays: list(String.t()),
|
||||||
|
steps: [(t -> t) | :assemble, ...]
|
||||||
|
}
|
||||||
|
|
||||||
|
@default_apps [kernel: :permanent, stdlib: :permanent, elixir: :permanent, sasl: :permanent]
|
||||||
|
@safe_modes [:permanent, :temporary, :transient]
|
||||||
|
@unsafe_modes [:load, :none]
|
||||||
|
@significant_chunks ~w(Atom AtU8 Attr Code StrT ImpT ExpT FunT LitT Line)c
|
||||||
|
@copy_app_dirs ["priv"]
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
@spec from_config!(atom, keyword, keyword) :: t
|
||||||
|
def from_config!(name, config, overrides) do
|
||||||
|
{name, apps, opts} = find_release(name, config)
|
||||||
|
|
||||||
|
unless Atom.to_string(name) =~ ~r/^[a-z][a-z0-9_]*$/ do
|
||||||
|
Mix.raise(
|
||||||
|
"Invalid release name. A release name must start with a lowercase ASCII letter, " <>
|
||||||
|
"followed by lowercase ASCII letters, numbers, or underscores, got: #{inspect(name)}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
opts =
|
||||||
|
[overwrite: false, quiet: false, strip_beams: true]
|
||||||
|
|> Keyword.merge(opts)
|
||||||
|
|> Keyword.merge(overrides)
|
||||||
|
|
||||||
|
{include_erts, opts} = Keyword.pop(opts, :include_erts, true)
|
||||||
|
{erts_source, erts_lib_dir, erts_version} = erts_data(include_erts)
|
||||||
|
|
||||||
|
deps_apps = Mix.Project.deps_apps()
|
||||||
|
loaded_apps = apps |> Keyword.keys() |> load_apps(deps_apps, %{}, erts_lib_dir, [], :root)
|
||||||
|
|
||||||
|
# Make sure IEx is either an active part of the release or add it as none.
|
||||||
|
{loaded_apps, apps} =
|
||||||
|
if Map.has_key?(loaded_apps, :iex) do
|
||||||
|
{loaded_apps, apps}
|
||||||
|
else
|
||||||
|
{load_apps([:iex], deps_apps, loaded_apps, erts_lib_dir, [], :root), apps ++ [iex: :none]}
|
||||||
|
end
|
||||||
|
|
||||||
|
start_boot = build_start_boot(loaded_apps, apps)
|
||||||
|
start_clean_boot = build_start_clean_boot(start_boot)
|
||||||
|
|
||||||
|
{path, opts} =
|
||||||
|
Keyword.pop_lazy(opts, :path, fn ->
|
||||||
|
Path.join([Mix.Project.build_path(config), "rel", Atom.to_string(name)])
|
||||||
|
end)
|
||||||
|
|
||||||
|
path = Path.absname(path)
|
||||||
|
|
||||||
|
{version, opts} =
|
||||||
|
Keyword.pop_lazy(opts, :version, fn ->
|
||||||
|
config[:version] ||
|
||||||
|
Mix.raise(
|
||||||
|
"No :version found. Please make sure a :version is set in your project definition " <>
|
||||||
|
"or inside the release the configuration"
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
|
||||||
|
version =
|
||||||
|
case version do
|
||||||
|
{:from_app, app} ->
|
||||||
|
Application.load(app)
|
||||||
|
version = Application.spec(app, :vsn)
|
||||||
|
|
||||||
|
if !version do
|
||||||
|
Mix.raise(
|
||||||
|
"Could not find version for #{inspect(app)}, please make sure the application exists"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
to_string(version)
|
||||||
|
|
||||||
|
"" ->
|
||||||
|
Mix.raise("The release :version cannot be an empty string")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
version
|
||||||
|
end
|
||||||
|
|
||||||
|
{config_providers, opts} = Keyword.pop(opts, :config_providers, [])
|
||||||
|
{steps, opts} = Keyword.pop(opts, :steps, [:assemble])
|
||||||
|
validate_steps!(steps)
|
||||||
|
|
||||||
|
%Mix.Release{
|
||||||
|
name: name,
|
||||||
|
version: version,
|
||||||
|
path: path,
|
||||||
|
version_path: Path.join([path, "releases", version]),
|
||||||
|
erts_source: erts_source,
|
||||||
|
erts_version: erts_version,
|
||||||
|
applications: loaded_apps,
|
||||||
|
boot_scripts: %{start: start_boot, start_clean: start_clean_boot},
|
||||||
|
config_providers: config_providers,
|
||||||
|
options: opts,
|
||||||
|
overlays: [],
|
||||||
|
steps: steps
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp find_release(name, config) do
|
||||||
|
{name, opts_fun_or_list} = lookup_release(name, config) || infer_release(config)
|
||||||
|
opts = if is_function(opts_fun_or_list, 0), do: opts_fun_or_list.(), else: opts_fun_or_list
|
||||||
|
{apps, opts} = Keyword.pop(opts, :applications, [])
|
||||||
|
|
||||||
|
if apps == [] and Mix.Project.umbrella?(config) do
|
||||||
|
bad_umbrella!()
|
||||||
|
end
|
||||||
|
|
||||||
|
app = Keyword.get(config, :app)
|
||||||
|
apps = Keyword.merge(@default_apps, apps)
|
||||||
|
|
||||||
|
if is_nil(app) or Keyword.has_key?(apps, app) do
|
||||||
|
{name, apps, opts}
|
||||||
|
else
|
||||||
|
{name, apps ++ [{app, :permanent}], opts}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp lookup_release(nil, config) do
|
||||||
|
case Keyword.get(config, :releases, []) do
|
||||||
|
[] ->
|
||||||
|
nil
|
||||||
|
|
||||||
|
[{name, opts}] ->
|
||||||
|
{name, opts}
|
||||||
|
|
||||||
|
[_ | _] ->
|
||||||
|
case Keyword.get(config, :default_release) do
|
||||||
|
nil ->
|
||||||
|
Mix.raise(
|
||||||
|
"\"mix release\" was invoked without a name but there are multiple releases. " <>
|
||||||
|
"Please call \"mix release NAME\" or set :default_release in your project configuration"
|
||||||
|
)
|
||||||
|
|
||||||
|
name ->
|
||||||
|
lookup_release(name, config)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp lookup_release(name, config) do
|
||||||
|
if opts = config[:releases][name] do
|
||||||
|
{name, opts}
|
||||||
|
else
|
||||||
|
found = Keyword.get(config, :releases, [])
|
||||||
|
|
||||||
|
Mix.raise(
|
||||||
|
"Unknown release #{inspect(name)}. " <>
|
||||||
|
"The available releases are: #{inspect(Keyword.keys(found))}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp infer_release(config) do
|
||||||
|
if Mix.Project.umbrella?(config) do
|
||||||
|
bad_umbrella!()
|
||||||
|
else
|
||||||
|
{Keyword.fetch!(config, :app), []}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp bad_umbrella! do
|
||||||
|
Mix.raise("""
|
||||||
|
Umbrella projects require releases to be explicitly defined with \
|
||||||
|
a non-empty applications key that chooses which umbrella children \
|
||||||
|
should be part of the releases:
|
||||||
|
|
||||||
|
releases: [
|
||||||
|
foo: [
|
||||||
|
applications: [child_app_foo: :permanent]
|
||||||
|
],
|
||||||
|
bar: [
|
||||||
|
applications: [child_app_bar: :permanent]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
Alternatively you can perform the release from the children applications
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp erts_data(erts_data) when is_function(erts_data) do
|
||||||
|
erts_data(erts_data.())
|
||||||
|
end
|
||||||
|
|
||||||
|
defp erts_data(false) do
|
||||||
|
{nil, :code.lib_dir(), :erlang.system_info(:version)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp erts_data(true) do
|
||||||
|
version = :erlang.system_info(:version)
|
||||||
|
{:filename.join(:code.root_dir(), 'erts-#{version}'), :code.lib_dir(), version}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp erts_data(erts_source) when is_binary(erts_source) do
|
||||||
|
if File.exists?(erts_source) do
|
||||||
|
[_, erts_version] = erts_source |> Path.basename() |> String.split("-")
|
||||||
|
erts_lib_dir = erts_source |> Path.dirname() |> Path.join("lib") |> to_charlist()
|
||||||
|
{to_charlist(erts_source), erts_lib_dir, to_charlist(erts_version)}
|
||||||
|
else
|
||||||
|
Mix.raise("Could not find ERTS system at #{inspect(erts_source)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp load_apps(apps, deps_apps, seen, otp_root, optional, type) do
|
||||||
|
for app <- apps, reduce: seen do
|
||||||
|
seen ->
|
||||||
|
# IO.inspect(app, label: ">>>>>> load_apps app")
|
||||||
|
if reentrant_seen = reentrant(seen, app, type) do
|
||||||
|
reentrant_seen
|
||||||
|
else
|
||||||
|
load_app(app, deps_apps, seen, otp_root, optional, type)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp reentrant(seen, app, type) do
|
||||||
|
properties = seen[app]
|
||||||
|
|
||||||
|
cond do
|
||||||
|
is_nil(properties) ->
|
||||||
|
nil
|
||||||
|
|
||||||
|
type != :root and properties[:type] != type ->
|
||||||
|
if properties[:type] == :root do
|
||||||
|
put_in(seen[app][:type], type)
|
||||||
|
else
|
||||||
|
Mix.raise(
|
||||||
|
"#{inspect(app)} is listed both as a regular application and as an included application"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
true ->
|
||||||
|
seen
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp load_app(app, deps_apps, seen, otp_root, optional, type) do
|
||||||
|
cond do
|
||||||
|
path = app not in deps_apps && otp_path(otp_root, app) ->
|
||||||
|
do_load_app(app, path, deps_apps, seen, otp_root, true, type)
|
||||||
|
|
||||||
|
path = code_path(app) ->
|
||||||
|
do_load_app(app, path, deps_apps, seen, otp_root, false, type)
|
||||||
|
|
||||||
|
app in optional ->
|
||||||
|
seen
|
||||||
|
|
||||||
|
true ->
|
||||||
|
Mix.raise("Could not find application #{inspect(app)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp otp_path(otp_root, app) do
|
||||||
|
path = Path.join(otp_root, "#{app}-*")
|
||||||
|
|
||||||
|
case Path.wildcard(path) do
|
||||||
|
[] -> nil
|
||||||
|
paths -> paths |> Enum.sort() |> List.last() |> to_charlist()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp code_path(app) do
|
||||||
|
case :code.lib_dir(app) do
|
||||||
|
{:error, :bad_name} -> nil
|
||||||
|
path -> path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp do_load_app(app, path, deps_apps, seen, otp_root, otp_app?, type) do
|
||||||
|
case :file.consult(Path.join(path, "ebin/#{app}.app")) do
|
||||||
|
{:ok, terms} ->
|
||||||
|
[{:application, ^app, properties}] = terms
|
||||||
|
value = [path: path, otp_app?: otp_app?, type: type] ++ properties
|
||||||
|
seen = Map.put(seen, app, value)
|
||||||
|
applications = Keyword.get(properties, :applications, [])
|
||||||
|
optional = Keyword.get(properties, :optional_applications, [])
|
||||||
|
seen = load_apps(applications, deps_apps, seen, otp_root, optional, :depended)
|
||||||
|
included_applications = Keyword.get(properties, :included_applications, [])
|
||||||
|
load_apps(included_applications, deps_apps, seen, otp_root, [], :included)
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
Mix.raise("Could not load #{app}.app. Reason: #{inspect(reason)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_start_boot(all_apps, specified_apps) do
|
||||||
|
specified_apps ++
|
||||||
|
Enum.sort(
|
||||||
|
for(
|
||||||
|
{app, props} <- all_apps,
|
||||||
|
not List.keymember?(specified_apps, app, 0),
|
||||||
|
do: {app, default_mode(props)}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp default_mode(props) do
|
||||||
|
if props[:type] == :included, do: :load, else: :permanent
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_start_clean_boot(boot) do
|
||||||
|
for({app, _mode} <- boot, do: {app, :none})
|
||||||
|
|> Keyword.put(:stdlib, :permanent)
|
||||||
|
|> Keyword.put(:kernel, :permanent)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_steps!(steps) do
|
||||||
|
valid_atoms = [:assemble, :tar]
|
||||||
|
|
||||||
|
if not is_list(steps) or Enum.any?(steps, &(&1 not in valid_atoms and not is_function(&1, 1))) do
|
||||||
|
Mix.raise("""
|
||||||
|
The :steps option must be a list of:
|
||||||
|
|
||||||
|
* anonymous function that receives one argument
|
||||||
|
* the atom :assemble or :tar
|
||||||
|
|
||||||
|
Got: #{inspect(steps)}
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
|
||||||
|
if Enum.count(steps, &(&1 == :assemble)) != 1 do
|
||||||
|
Mix.raise("The :steps option must contain the atom :assemble once, got: #{inspect(steps)}")
|
||||||
|
end
|
||||||
|
|
||||||
|
if :assemble in Enum.drop_while(steps, &(&1 != :tar)) do
|
||||||
|
Mix.raise("The :tar step must come after :assemble")
|
||||||
|
end
|
||||||
|
|
||||||
|
if Enum.count(steps, &(&1 == :tar)) > 1 do
|
||||||
|
Mix.raise("The :steps option can only contain the atom :tar once")
|
||||||
|
end
|
||||||
|
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Makes the `sys.config` structure.
|
||||||
|
|
||||||
|
If there are config providers, then a value is injected into
|
||||||
|
the `:elixir` application configuration in `sys_config` to be
|
||||||
|
read during boot and trigger the providers.
|
||||||
|
|
||||||
|
It uses the following release options to customize its behaviour:
|
||||||
|
|
||||||
|
* `:reboot_system_after_config`
|
||||||
|
* `:start_distribution_during_config`
|
||||||
|
* `:prune_runtime_sys_config_after_boot`
|
||||||
|
|
||||||
|
In case there are no config providers, it doesn't change `sys_config`.
|
||||||
|
"""
|
||||||
|
@spec make_sys_config(t, keyword(), Config.Provider.config_path()) ::
|
||||||
|
:ok | {:error, String.t()}
|
||||||
|
def make_sys_config(release, sys_config, config_provider_path) do
|
||||||
|
{sys_config, runtime_config?} =
|
||||||
|
merge_provider_config(release, sys_config, config_provider_path)
|
||||||
|
|
||||||
|
path = Path.join(release.version_path, "sys.config")
|
||||||
|
|
||||||
|
args = [runtime_config?, sys_config]
|
||||||
|
format = "%% coding: utf-8~n%% RUNTIME_CONFIG=~s~n~tw.~n"
|
||||||
|
File.mkdir_p!(Path.dirname(path))
|
||||||
|
File.write!(path, IO.chardata_to_string(:io_lib.format(format, args)))
|
||||||
|
|
||||||
|
case :file.consult(path) do
|
||||||
|
{:ok, _} ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
invalid =
|
||||||
|
for {app, kv} <- sys_config,
|
||||||
|
{key, value} <- kv,
|
||||||
|
not valid_config?(value),
|
||||||
|
do: """
|
||||||
|
|
||||||
|
Application: #{inspect(app)}
|
||||||
|
Key: #{inspect(key)}
|
||||||
|
Value: #{inspect(value)}
|
||||||
|
"""
|
||||||
|
|
||||||
|
message =
|
||||||
|
case invalid do
|
||||||
|
[] ->
|
||||||
|
"Could not read configuration file. Reason: #{inspect(reason)}"
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
"Could not read configuration file. It has invalid configuration terms " <>
|
||||||
|
"such as functions, references, and pids. Please make sure your configuration " <>
|
||||||
|
"is made of numbers, atoms, strings, maps, tuples and lists. The following entries " <>
|
||||||
|
"are wrong:\n#{Enum.join(invalid)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
{:error, message}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp valid_config?(m) when is_map(m),
|
||||||
|
do: Enum.all?(Map.delete(m, :__struct__), &valid_config?/1)
|
||||||
|
|
||||||
|
defp valid_config?(l) when is_list(l), do: Enum.all?(l, &valid_config?/1)
|
||||||
|
defp valid_config?(t) when is_tuple(t), do: Enum.all?(Tuple.to_list(t), &valid_config?/1)
|
||||||
|
defp valid_config?(o), do: is_number(o) or is_atom(o) or is_binary(o)
|
||||||
|
|
||||||
|
defp merge_provider_config(%{config_providers: []}, sys_config, _), do: {sys_config, false}
|
||||||
|
|
||||||
|
defp merge_provider_config(release, sys_config, config_path) do
|
||||||
|
{reboot?, extra_config, initial_config} = start_distribution(release)
|
||||||
|
|
||||||
|
prune_runtime_sys_config_after_boot =
|
||||||
|
Keyword.get(release.options, :prune_runtime_sys_config_after_boot, false)
|
||||||
|
|
||||||
|
opts = [
|
||||||
|
extra_config: initial_config,
|
||||||
|
prune_runtime_sys_config_after_boot: prune_runtime_sys_config_after_boot,
|
||||||
|
reboot_system_after_config: reboot?,
|
||||||
|
validate_compile_env: validate_compile_env(release)
|
||||||
|
]
|
||||||
|
|
||||||
|
init_config = Config.Provider.init(release.config_providers, config_path, opts)
|
||||||
|
{Config.Reader.merge(sys_config, init_config ++ extra_config), reboot?}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_compile_env(release) do
|
||||||
|
with true <- Keyword.get(release.options, :validate_compile_env, true),
|
||||||
|
[_ | _] = compile_env <- compile_env(release) do
|
||||||
|
compile_env
|
||||||
|
else
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp compile_env(release) do
|
||||||
|
for {_, properties} <- release.applications,
|
||||||
|
triplet <- Keyword.get(properties, :compile_env, []),
|
||||||
|
do: triplet
|
||||||
|
end
|
||||||
|
|
||||||
|
defp start_distribution(%{options: opts}) do
|
||||||
|
reboot? = Keyword.get(opts, :reboot_system_after_config, false)
|
||||||
|
early_distribution? = Keyword.get(opts, :start_distribution_during_config, false)
|
||||||
|
|
||||||
|
if not reboot? or early_distribution? do
|
||||||
|
{reboot?, [], []}
|
||||||
|
else
|
||||||
|
{true, [kernel: [start_distribution: false]], [kernel: [start_distribution: true]]}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Copies the cookie to the given path.
|
||||||
|
|
||||||
|
If a cookie option was given, we compare it with
|
||||||
|
the contents of the file (if any), and ask the user
|
||||||
|
if they want to override.
|
||||||
|
|
||||||
|
If there is no option, we generate a random one
|
||||||
|
the first time.
|
||||||
|
"""
|
||||||
|
@spec make_cookie(t, Path.t()) :: :ok
|
||||||
|
def make_cookie(release, path) do
|
||||||
|
cond do
|
||||||
|
cookie = release.options[:cookie] ->
|
||||||
|
Mix.Generator.create_file(path, cookie, quiet: true)
|
||||||
|
:ok
|
||||||
|
|
||||||
|
File.exists?(path) ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
true ->
|
||||||
|
File.write!(path, random_cookie())
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp random_cookie, do: Base.encode32(:crypto.strong_rand_bytes(32))
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Makes the start_erl.data file with the
|
||||||
|
ERTS version and release versions.
|
||||||
|
"""
|
||||||
|
@spec make_start_erl(t, Path.t()) :: :ok
|
||||||
|
def make_start_erl(release, path) do
|
||||||
|
File.write!(path, "#{release.erts_version} #{release.version}")
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Makes boot scripts.
|
||||||
|
|
||||||
|
It receives a path to the boot file, without extension, such as
|
||||||
|
`releases/0.1.0/start` and this command will write `start.rel`,
|
||||||
|
`start.boot`, and `start.script` to the given path, returning
|
||||||
|
`{:ok, rel_path}` or `{:error, message}`.
|
||||||
|
|
||||||
|
The boot script uses the RELEASE_LIB environment variable, which must
|
||||||
|
be accordingly set with `--boot-var` and point to the release lib dir.
|
||||||
|
"""
|
||||||
|
@spec make_boot_script(t, Path.t(), [{application(), mode()}], [String.t()]) ::
|
||||||
|
:ok | {:error, String.t()}
|
||||||
|
def make_boot_script(release, path, modes, prepend_paths \\ []) do
|
||||||
|
with {:ok, rel_spec} <- build_release_spec(release, modes) do
|
||||||
|
File.write!(path <> ".rel", consultable(rel_spec))
|
||||||
|
|
||||||
|
sys_path = String.to_charlist(path)
|
||||||
|
|
||||||
|
sys_options = [
|
||||||
|
:silent,
|
||||||
|
:no_dot_erlang,
|
||||||
|
:no_warn_sasl,
|
||||||
|
variables: build_variables(release),
|
||||||
|
path: build_paths(release)
|
||||||
|
]
|
||||||
|
|
||||||
|
case :systools.make_script(sys_path, sys_options) do
|
||||||
|
{:ok, _module, _warnings} ->
|
||||||
|
script_path = sys_path ++ '.script'
|
||||||
|
{:ok, [{:script, rel_info, instructions}]} = :file.consult(script_path)
|
||||||
|
|
||||||
|
instructions =
|
||||||
|
instructions
|
||||||
|
|> post_stdlib_applies(release)
|
||||||
|
|> prepend_paths_to_script(prepend_paths)
|
||||||
|
|
||||||
|
script = {:script, rel_info, instructions}
|
||||||
|
File.write!(script_path, consultable(script))
|
||||||
|
:ok = :systools.script2boot(sys_path)
|
||||||
|
|
||||||
|
{:error, module, info} ->
|
||||||
|
message = module.format_error(info) |> to_string() |> String.trim()
|
||||||
|
{:error, message}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_variables(release) do
|
||||||
|
for {_, properties} <- release.applications,
|
||||||
|
not Keyword.fetch!(properties, :otp_app?),
|
||||||
|
uniq: true,
|
||||||
|
do: {'RELEASE_LIB', properties |> Keyword.fetch!(:path) |> :filename.dirname()}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_paths(release) do
|
||||||
|
for {_, properties} <- release.applications,
|
||||||
|
Keyword.fetch!(properties, :otp_app?),
|
||||||
|
do: properties |> Keyword.fetch!(:path) |> Path.join("ebin") |> to_charlist()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_release_spec(release, modes) do
|
||||||
|
%{name: name, version: version, erts_version: erts_version, applications: apps} = release
|
||||||
|
|
||||||
|
rel_apps =
|
||||||
|
for {app, mode} <- modes do
|
||||||
|
properties = Map.get(apps, app) || throw({:error, "Unknown application #{inspect(app)}"})
|
||||||
|
children = Keyword.get(properties, :applications, [])
|
||||||
|
# validate_mode!(app, mode, modes, children)
|
||||||
|
build_app_for_release(app, mode, properties)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, {:release, {to_charlist(name), to_charlist(version)}, {:erts, erts_version}, rel_apps}}
|
||||||
|
catch
|
||||||
|
{:error, message} -> {:error, message}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp validate_mode!(app, mode, modes, children) do
|
||||||
|
safe_mode? = mode in @safe_modes
|
||||||
|
|
||||||
|
if not safe_mode? and mode not in @unsafe_modes do
|
||||||
|
throw(
|
||||||
|
{:error,
|
||||||
|
"Unknown mode #{inspect(mode)} for #{inspect(app)}. " <>
|
||||||
|
"Valid modes are: #{inspect(@safe_modes ++ @unsafe_modes)}"}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
for child <- children do
|
||||||
|
child_mode = Keyword.get(modes, child)
|
||||||
|
|
||||||
|
cond do
|
||||||
|
is_nil(child_mode) ->
|
||||||
|
throw(
|
||||||
|
{:error,
|
||||||
|
"Application #{inspect(app)} is listed in the release boot, " <>
|
||||||
|
"but it depends on #{inspect(child)}, which isn't"}
|
||||||
|
)
|
||||||
|
|
||||||
|
safe_mode? and child_mode in @unsafe_modes ->
|
||||||
|
throw(
|
||||||
|
{:error,
|
||||||
|
"""
|
||||||
|
Application #{inspect(app)} has mode #{inspect(mode)} but it depends on \
|
||||||
|
#{inspect(child)} which is set to #{inspect(child_mode)}. If you really want \
|
||||||
|
to set such mode for #{inspect(child)} make sure that all applications that depend \
|
||||||
|
on it are also set to :load or :none, otherwise your release will fail to boot
|
||||||
|
"""}
|
||||||
|
)
|
||||||
|
|
||||||
|
true ->
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp build_app_for_release(app, mode, properties) do
|
||||||
|
vsn = Keyword.fetch!(properties, :vsn)
|
||||||
|
|
||||||
|
case Keyword.get(properties, :included_applications, []) do
|
||||||
|
[] -> {app, vsn, mode}
|
||||||
|
included_apps -> {app, vsn, mode, included_apps}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp post_stdlib_applies(instructions, release) do
|
||||||
|
{pre, [stdlib | post]} =
|
||||||
|
Enum.split_while(
|
||||||
|
instructions,
|
||||||
|
&(not match?({:apply, {:application, :start_boot, [:stdlib, _]}}, &1))
|
||||||
|
)
|
||||||
|
|
||||||
|
pre ++ [stdlib] ++ config_provider_apply(release) ++ post
|
||||||
|
end
|
||||||
|
|
||||||
|
defp config_provider_apply(%{config_providers: []}),
|
||||||
|
do: []
|
||||||
|
|
||||||
|
defp config_provider_apply(_),
|
||||||
|
do: [{:apply, {Config.Provider, :boot, []}}]
|
||||||
|
|
||||||
|
defp prepend_paths_to_script(instructions, []), do: instructions
|
||||||
|
|
||||||
|
defp prepend_paths_to_script(instructions, prepend_paths) do
|
||||||
|
prepend_paths = Enum.map(prepend_paths, &String.to_charlist/1)
|
||||||
|
|
||||||
|
Enum.map(instructions, fn
|
||||||
|
{:path, paths} ->
|
||||||
|
if Enum.any?(paths, &List.starts_with?(&1, '$RELEASE_LIB')) do
|
||||||
|
{:path, prepend_paths ++ paths}
|
||||||
|
else
|
||||||
|
{:path, paths}
|
||||||
|
end
|
||||||
|
|
||||||
|
other ->
|
||||||
|
other
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp consultable(term) do
|
||||||
|
IO.chardata_to_string(:io_lib.format("%% coding: utf-8~n~tp.~n", [term]))
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Finds a template path for the release.
|
||||||
|
"""
|
||||||
|
def rel_templates_path(release, path) do
|
||||||
|
Path.join(release.options[:rel_templates_path] || "rel", path)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Copies ERTS if the release is configured to do so.
|
||||||
|
|
||||||
|
Returns true if the release was copied, false otherwise.
|
||||||
|
"""
|
||||||
|
@spec copy_erts(t) :: boolean()
|
||||||
|
def copy_erts(%{erts_source: nil}) do
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def copy_erts(release) do
|
||||||
|
destination = Path.join(release.path, "erts-#{release.erts_version}/bin")
|
||||||
|
File.mkdir_p!(destination)
|
||||||
|
|
||||||
|
release.erts_source
|
||||||
|
|> Path.join("bin")
|
||||||
|
|> File.cp_r!(destination, fn _, _ -> false end)
|
||||||
|
|
||||||
|
_ = File.rm(Path.join(destination, "erl"))
|
||||||
|
_ = File.rm(Path.join(destination, "erl.ini"))
|
||||||
|
|
||||||
|
destination
|
||||||
|
|> Path.join("erl")
|
||||||
|
|> File.write!(~S"""
|
||||||
|
#!/bin/sh
|
||||||
|
SELF=$(readlink "$0" || true)
|
||||||
|
if [ -z "$SELF" ]; then SELF="$0"; fi
|
||||||
|
BINDIR="$(cd "$(dirname "$SELF")" && pwd -P)"
|
||||||
|
ROOTDIR="${ERL_ROOTDIR:-"$(dirname "$(dirname "$BINDIR")")"}"
|
||||||
|
EMU=beam
|
||||||
|
PROGNAME=$(echo "$0" | sed 's/.*\///')
|
||||||
|
export EMU
|
||||||
|
export ROOTDIR
|
||||||
|
export BINDIR
|
||||||
|
export PROGNAME
|
||||||
|
exec "$BINDIR/erlexec" ${1+"$@"}
|
||||||
|
""")
|
||||||
|
|
||||||
|
File.chmod!(Path.join(destination, "erl"), 0o755)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Copies the given application specification into the release.
|
||||||
|
|
||||||
|
It assumes the application exists in the release.
|
||||||
|
"""
|
||||||
|
@spec copy_app(t, application) :: boolean()
|
||||||
|
def copy_app(release, app) do
|
||||||
|
properties = Map.fetch!(release.applications, app)
|
||||||
|
vsn = Keyword.fetch!(properties, :vsn)
|
||||||
|
|
||||||
|
source_app = Keyword.fetch!(properties, :path)
|
||||||
|
target_app = Path.join([release.path, "lib", "#{app}-#{vsn}"])
|
||||||
|
|
||||||
|
if is_nil(release.erts_source) and Keyword.fetch!(properties, :otp_app?) do
|
||||||
|
false
|
||||||
|
else
|
||||||
|
File.rm_rf!(target_app)
|
||||||
|
File.mkdir_p!(target_app)
|
||||||
|
|
||||||
|
copy_ebin(release, Path.join(source_app, "ebin"), Path.join(target_app, "ebin"))
|
||||||
|
|
||||||
|
for dir <- @copy_app_dirs do
|
||||||
|
source_dir = Path.join(source_app, dir)
|
||||||
|
target_dir = Path.join(target_app, dir)
|
||||||
|
|
||||||
|
source_dir =
|
||||||
|
case File.read_link(source_dir) do
|
||||||
|
{:ok, link_target} -> Path.expand(link_target, source_app)
|
||||||
|
_ -> source_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
File.exists?(source_dir) && File.cp_r!(source_dir, target_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Copies the ebin directory at `source` to `target`
|
||||||
|
respecting release options such a `:strip_beams`.
|
||||||
|
"""
|
||||||
|
@spec copy_ebin(t, Path.t(), Path.t()) :: boolean()
|
||||||
|
def copy_ebin(release, source, target) do
|
||||||
|
with {:ok, [_ | _] = files} <- File.ls(source) do
|
||||||
|
File.mkdir_p!(target)
|
||||||
|
|
||||||
|
strip_options =
|
||||||
|
release.options
|
||||||
|
|> Keyword.get(:strip_beams, true)
|
||||||
|
|> parse_strip_beams_options()
|
||||||
|
|
||||||
|
for file <- files do
|
||||||
|
source_file = Path.join(source, file)
|
||||||
|
target_file = Path.join(target, file)
|
||||||
|
|
||||||
|
with true <- is_list(strip_options) and String.ends_with?(file, ".beam"),
|
||||||
|
{:ok, binary} <- strip_beam(File.read!(source_file), strip_options) do
|
||||||
|
File.write!(target_file, binary)
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
# Use File.cp!/3 to preserve file mode for any executables stored
|
||||||
|
# in the ebin directory.
|
||||||
|
File.cp!(source_file, target_file)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
true
|
||||||
|
else
|
||||||
|
_ -> false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Strips a beam file for a release.
|
||||||
|
|
||||||
|
This keeps only significant chunks necessary for the VM operation,
|
||||||
|
discarding documentation, debug info, compile information and others.
|
||||||
|
|
||||||
|
The exact chunks that are kept are not documented and may change in
|
||||||
|
future versions.
|
||||||
|
"""
|
||||||
|
@spec strip_beam(binary(), keyword()) :: {:ok, binary()} | {:error, :beam_lib, term()}
|
||||||
|
def strip_beam(binary, options \\ []) when is_list(options) do
|
||||||
|
chunks_to_keep = options[:keep] |> List.wrap() |> Enum.map(&String.to_charlist/1)
|
||||||
|
all_chunks = Enum.uniq(@significant_chunks ++ chunks_to_keep)
|
||||||
|
|
||||||
|
case :beam_lib.chunks(binary, all_chunks, [:allow_missing_chunks]) do
|
||||||
|
{:ok, {_, chunks}} ->
|
||||||
|
chunks = for {name, chunk} <- chunks, is_binary(chunk), do: {name, chunk}
|
||||||
|
{:ok, binary} = :beam_lib.build_module(chunks)
|
||||||
|
{:ok, :zlib.gzip(binary)}
|
||||||
|
|
||||||
|
{:error, _, _} = error ->
|
||||||
|
error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp parse_strip_beams_options(options) do
|
||||||
|
case options do
|
||||||
|
options when is_list(options) -> options
|
||||||
|
true -> []
|
||||||
|
false -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
mix release --overwrite
|
mix release --overwrite
|
||||||
cp -r apps/emqx/etc/certs _build/dev/rel/emqx/etc/certs
|
cp -r apps/emqx/etc/certs _build/dev/rel/emqx/etc/certs
|
||||||
cp temp/sys.config _build/dev/rel/emqx/releases/5.0.0-beta.2-3fdc075b/
|
cp temp/sys.config _build/dev/rel/emqx/releases/5.0.0-beta.2-3fdc075b/
|
||||||
|
|
Loading…
Reference in New Issue