Packages
exrm
0.12.11
1.0.8
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc8
1.0.0-rc7
1.0.0-rc6
1.0.0-rc5
1.0.0-rc4
1.0.0-rc3
0.19.9
0.19.8
0.19.7
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.8
0.18.7
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.3
0.17.2
0.17.1
0.17.0
0.16.0
0.15.3
0.15.2
0.15.1
0.15.0
0.14.17
0.14.16
0.14.15
0.14.14
0.14.13
0.14.12
0.14.11
0.14.10
0.14.9
0.14.8
0.14.7
0.14.6
0.14.5
0.14.3
0.14.2
0.14.1
0.14.0
0.12.12
0.12.11
0.12.10
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.1
0.10.0
0.9.2
0.9.1
0.8.1
0.8.0
0.7.3
0.7.2
0.7.1
0.7.0
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.0
0.4.7
Exrm, or Elixir Release Manager, provides mix tasks for building, upgrading, and controlling release packages for your application.
Current section
Files
Jump to
Current section
Files
lib/exrm/appups.ex
defmodule ReleaseManager.Appups do
@moduledoc """
Module for auto-generating appups between releases.
"""
import ReleaseManager.Utils, only: [write_term: 2]
@doc """
Generate a .appup for the given application, start version, and upgrade version.
## Parameter information
application: the application name as an atom
v1: the start version, such as "0.0.1"
v2: the upgrade version, such as "0.0.2"
v1_path: the path to the v1 artifacts (rel/<app>/lib/<app>-0.0.1)
v2_path: the path to the v2 artifacts (_build/prod/lib/<app>)
"""
def make(application, v1, v2, v1_path, v2_path) do
v1_release =
v1_path
|> Path.join("/ebin/")
|> Path.join(Atom.to_string(application) <> ".app")
|> String.to_char_list
v2_release =
v2_path
|> Path.join("/ebin/")
|> Path.join(Atom.to_string(application) <> ".app")
|> String.to_char_list
case :file.consult(v1_release) do
{ :ok, [ { :application, ^application, v1_props } ] } ->
case vsn(v1_props) === v1 do
true ->
case :file.consult(v2_release) do
{ :ok, [ { :application, ^application, v2_props } ] } ->
case vsn(v2_props) === v2 do
true ->
make_appup(application, v1, v1_props, v2, v2_path, v2_props)
false ->
{ :error, :bad_new_appvsn }
end
_ ->
{ :error, :bad_new_appfile }
end
false ->
{ :error, :bad_old_appvsn }
end
_ ->
{ :error, :bad_old_appfile }
end
end
defp make_appup(application, v1, v1_props, v2, v2_path, v2_props) do
add_mods = modules(v2_props) -- modules(v1_props)
del_mods = modules(v1_props) -- modules(v2_props)
{ up_version_change, down_version_change } =
case start_module(v2_props) do
{ :ok, start_mod, start_args } ->
start_mod_beam_file =
v2_path
|> Path.join("/ebin/")
|> Path.join(Atom.to_string(start_mod) <> ".beam")
{ start_mod_beam_file |> File.read! |> version_change(v1, start_mod, start_args),
start_mod_beam_file |> File.read! |> version_change({:down, v1}, start_mod, start_args) }
:undefined ->
{ [], [] }
end
up_directives =
(modules(v2_props) -- add_mods) |> Enum.map(fn module ->
(v2_path <> "/ebin/" <> Atom.to_string(module) <> ".beam")
|> File.read!
|> upgrade_directives(v1, v2, module)
end) |> List.flatten
down_directives =
Enum.reverse(modules(v2_props) -- add_mods) |> Enum.map(fn module ->
(v2_path <> "/ebin/" <> Atom.to_string(module) <> ".beam")
|> File.read!
|> downgrade_directives(v1, v2, module)
end) |> List.flatten
appup =
{ v2 |> String.to_char_list,
[ { v1 |> String.to_char_list,
(for m <- add_mods, do: { :add_module, m })
++ up_directives
++ up_version_change
++ (for m <- del_mods, do: { :delete_module, m })
}
],
[ { v1 |> String.to_char_list,
(for m <- :lists.reverse(del_mods), do: { :add_module, m })
++ down_version_change
++ down_directives
++ (for m <- :lists.reverse(add_mods), do: { :delete_module, m })
}
]
}
# Save the appup to the upgrade's build directory
v2_path
|> Path.join("ebin")
|> Path.join((application |> Atom.to_string) <> ".appup")
|> write_term(appup)
{ :ok, appup }
end
defp version_change(beam, from, start_mod, start_args) do
case has_version_change(beam) do
true ->
[ { :apply, { start_mod, :version_change, [ from, start_args ] } } ]
false ->
[]
end
end
defp has_version_change(beam) do
beam_exports(beam, :version_change, 2)
end
defp beam_exports(beam, func, arity) do
case :beam_lib.chunks(beam, [ :exports ]) do
{ :ok, { _, [ { :exports, exports } ] } } ->
exports |> Enum.member?({ func, arity })
_ ->
false
end
end
defp start_module(props) do
case :lists.keysearch(:mod, 1, props) do
{ :value, { :mod, { start_mod, start_args } } } ->
{ :ok, start_mod, start_args }
false ->
:undefined
end
end
defp modules(props) do
{ :value, { :modules, modules } } = :lists.keysearch(:modules, 1, props)
modules
end
defp upgrade_directives(beam, v1, v2, m) do
case is_supervisor(beam) do
true ->
upgrade_directives_supervisor(beam, v1, v2, m)
false ->
case has_code_change(beam) do
true -> [ { :update, m, :infinity, { :advanced, [] }, :brutal_purge, :brutal_purge, [] } ]
false -> [ { :load_module, m } ]
end
end
end
defp upgrade_directives_supervisor(beam, v1, v2, m) do
case beam_exports(beam, :sup_upgrade_notify, 2) do
true ->
[ { :update, m, :supervisor },
{ :apply, { m, :sup_upgrade_notify, [ v1, v2 ] } } ]
false ->
[ { :update, m, :supervisor } ]
end
end
defp downgrade_directives(beam, v1, v2, m) do
case is_supervisor(beam) do
true ->
downgrade_directives_supervisor(beam, v1, v2, m)
false ->
case has_code_change(beam) do
true -> [ { :update, m, :infinity, { :advanced, [] }, :brutal_purge, :brutal_purge, [] } ]
false -> [ { :load_module, m } ]
end
end
end
defp downgrade_directives_supervisor(beam, v1, v2, m) do
case beam_exports(beam, :sup_downgrade_notify, 2) do
true ->
[ {
:apply, { m, :sup_downgrade_notify, [ v1, v2 ] }
},
{
:update, m, :supervisor
} ]
false ->
[ { :update, m, :supervisor } ]
end
end
defp has_code_change(beam) do
beam_exports(beam, :code_change, 3)
end
defp is_supervisor(beam) do
case :beam_lib.chunks(beam, [ :attributes ]) do
{ :ok, { _, [ { :attributes, attr } ] } } ->
has_element(attr, :behaviour, :supervisor) or has_element(attr, :behavior, :supervisor)
_ ->
false
end
end
defp vsn(props) do
{ :value, { :vsn, vsn } } = :lists.keysearch(:vsn, 1, props)
vsn |> List.to_string
end
defp has_element(attr, key, elem) do
case :lists.keysearch(key, 1, attr) do
{ :value, { ^key, value } } ->
:lists.member(elem, value)
_ ->
false
end
end
end