diff options
author | Gary Rennie <gazler@gmail.com> | 2019-08-20 00:22:26 +0100 |
---|---|---|
committer | Eric Meadows-Jönsson <eric.meadows.jonsson@gmail.com> | 2019-08-19 16:22:26 -0700 |
commit | d44da48ee108de4166d961d5bf94dd4c8584f0f2 (patch) | |
tree | d32aa913006e2c9816231f33af37e54e79d04727 | |
parent | 59605b459f9623cfb3ecb9e958276030a6368f06 (diff) | |
download | elixir-d44da48ee108de4166d961d5bf94dd4c8584f0f2.tar.gz |
Add :tar option for releases to create a tarball (#9290)
-rw-r--r-- | lib/mix/lib/mix/release.ex | 17 | ||||
-rw-r--r-- | lib/mix/lib/mix/tasks/release.ex | 36 | ||||
-rw-r--r-- | lib/mix/test/mix/release_test.exs | 8 | ||||
-rw-r--r-- | lib/mix/test/mix/tasks/release_test.exs | 43 |
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 -> |