summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGary Rennie <gazler@gmail.com>2019-08-20 00:22:26 +0100
committerEric Meadows-Jönsson <eric.meadows.jonsson@gmail.com>2019-08-19 16:22:26 -0700
commitd44da48ee108de4166d961d5bf94dd4c8584f0f2 (patch)
treed32aa913006e2c9816231f33af37e54e79d04727
parent59605b459f9623cfb3ecb9e958276030a6368f06 (diff)
downloadelixir-d44da48ee108de4166d961d5bf94dd4c8584f0f2.tar.gz
Add :tar option for releases to create a tarball (#9290)
-rw-r--r--lib/mix/lib/mix/release.ex17
-rw-r--r--lib/mix/lib/mix/tasks/release.ex36
-rw-r--r--lib/mix/test/mix/release_test.exs8
-rw-r--r--lib/mix/test/mix/tasks/release_test.exs43
4 files changed, 100 insertions, 4 deletions
diff --git a/lib/mix/lib/mix/release.ex b/lib/mix/lib/mix/release.ex
index 146273b3f..ce6099354 100644
--- a/lib/mix/lib/mix/release.ex
+++ b/lib/mix/lib/mix/release.ex
@@ -25,7 +25,8 @@ defmodule Mix.Release do
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
* `: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
+ 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 [
@@ -335,12 +336,14 @@ defmodule Mix.Release do
end
defp validate_steps!(steps) do
- if not is_list(steps) or Enum.any?(steps, &(&1 != :assemble and not is_function(&1, 1))) 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
+ * the atom :assemble or :tar
Got: #{inspect(steps)}
""")
@@ -350,6 +353,14 @@ defmodule Mix.Release 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
diff --git a/lib/mix/lib/mix/tasks/release.ex b/lib/mix/lib/mix/tasks/release.ex
index b555ee500..413872f53 100644
--- a/lib/mix/lib/mix/tasks/release.ex
+++ b/lib/mix/lib/mix/tasks/release.ex
@@ -492,7 +492,9 @@ defmodule Mix.Tasks.Release do
can pass anonymous functions before and after the `:assemble` to
customize your release assembling pipeline. Those anonymous functions
will receive a `Mix.Release` struct and must return the same or
- an updated `Mix.Release` struct.
+ an updated `Mix.Release` struct. It is also possible to build a tarball
+ of the release by passing the `:tar` step anywhere after `:assemble`.
+ The tarball is created in `_build/MIX_ENV/rel/RELEASE_NAME-RELEASE_VSN.tar.gz`
See `Mix.Release` for more documentation on the struct and which
fields can be modified. Note that `:steps` field itself can be
@@ -954,6 +956,10 @@ defmodule Mix.Tasks.Release do
end
end
+ defp run_steps(%{steps: [:tar | steps]} = release) do
+ %{release | steps: steps} |> make_tar() |> run_steps()
+ end
+
defp run_steps(%{steps: [:assemble | steps]} = release) do
%{release | steps: steps} |> assemble() |> run_steps()
end
@@ -1007,6 +1013,34 @@ defmodule Mix.Tasks.Release do
release
end
+ defp make_tar(release) do
+ tar_filename = "#{release.name}-#{release.version}.tar.gz"
+ out_path = Path.join([release.path, "..", "..", tar_filename]) |> Path.expand()
+ info(release, [:green, "* building ", :reset, out_path])
+
+ lib_dirs =
+ Enum.reduce(release.applications, [], fn {name, app_config}, acc ->
+ vsn = Keyword.fetch!(app_config, :vsn)
+ [Path.join("lib", "#{name}-#{vsn}") | acc]
+ end)
+
+ release_files =
+ for basename <- File.ls!(Path.join(release.path, "releases")),
+ not File.dir?(Path.join([release.path, "releases", basename])),
+ do: Path.join("releases", basename)
+
+ dirs =
+ ["bin", Path.join("releases", release.version), "erts-#{release.erts_version}"] ++
+ lib_dirs ++ release_files
+
+ files =
+ Enum.map(dirs, &{String.to_charlist(&1), String.to_charlist(Path.join(release.path, &1))})
+
+ File.rm(out_path)
+ :ok = :erl_tar.create(String.to_charlist(out_path), files, [:dereference, :compressed])
+ release
+ end
+
# build_rel
defp build_rel(release, config) do
diff --git a/lib/mix/test/mix/release_test.exs b/lib/mix/test/mix/release_test.exs
index d9dfd6087..59044219d 100644
--- a/lib/mix/test/mix/release_test.exs
+++ b/lib/mix/test/mix/release_test.exs
@@ -184,6 +184,14 @@ defmodule Mix.ReleaseTest do
fn -> release(steps: [:assemble, :assemble]) end
assert_raise Mix.Error,
+ ~r"The :tar step must come after :assemble",
+ fn -> release(steps: [:tar, :assemble]) end
+
+ assert_raise Mix.Error,
+ ~r"The :steps option can only contain the atom :tar once",
+ fn -> release(steps: [:assemble, :tar, :tar]) end
+
+ assert_raise Mix.Error,
~r"The :steps option must be",
fn -> release(steps: [:foo]) end
end
diff --git a/lib/mix/test/mix/tasks/release_test.exs b/lib/mix/test/mix/tasks/release_test.exs
index 793347d73..dde079717 100644
--- a/lib/mix/test/mix/tasks/release_test.exs
+++ b/lib/mix/test/mix/tasks/release_test.exs
@@ -36,6 +36,49 @@ defmodule Mix.Tasks.ReleaseTest do
end)
end
+ test "tar" do
+ in_fixture("release_test", fn ->
+ config = [releases: [demo: [steps: [:assemble, :tar]]]]
+
+ Mix.Project.in_project(:release_test, ".", config, fn _ ->
+ root = Path.absname("_build/#{Mix.env()}/rel/demo")
+
+ ignored_app_path = Path.join([root, "lib", "ignored_app-0.1.0", "ebin"])
+ File.mkdir_p!(ignored_app_path)
+ File.touch(Path.join(ignored_app_path, "ignored_app.app"))
+
+ ignored_release_path = Path.join([root, "releases", "ignored_dir"])
+ File.mkdir_p!(ignored_release_path)
+ File.touch(Path.join(ignored_release_path, "ignored"))
+
+ Mix.Task.run("release")
+ tar_path = Path.expand(Path.join([root, "..", "..", "demo-0.1.0.tar.gz"]))
+ message = "* building #{tar_path}"
+ assert_received {:mix_shell, :info, [^message]}
+ assert File.exists?(tar_path)
+
+ {:ok, files} = String.to_charlist(tar_path) |> :erl_tar.table([:compressed])
+
+ files = Enum.map(files, &to_string/1)
+ files_with_versions = File.ls!(Path.join(root, "lib"))
+
+ assert "bin/demo" in files
+ assert "releases/0.1.0/sys.config" in files
+ assert "releases/0.1.0/vm.args" in files
+ assert "releases/COOKIE" in files
+ assert "releases/start_erl.data" in files
+
+ for dir <- files_with_versions -- ["ignored_app-0.1.0"] do
+ [name | _] = String.split(dir, "-")
+ assert "lib/#{dir}/ebin/#{name}.app" in files
+ end
+
+ refute "lib/ignored_app-0.1.0/ebin/ignored_app.app" in files
+ refute "releases/ignored_dir/ignored" in files
+ end)
+ end)
+ end
+
test "steps" do
in_fixture("release_test", fn ->
last_step = fn release ->