Compare commits

...
10 Commits
Author SHA1 Message Date
Dmitry Kakurin 762989b39f Fix Path.absname/1 to correctly handle UNC paths on Windows (#9689) 2020-01-06 21:44:27 +01:00
Lasse Skindstad Ebert 1767df4747 Fix release tar when include_erts is false (#9570)
Now excludes files that does not exist on the file system. This could
happen e.g. if the release is built with `include_erts: false`.

Otherwise building the tar would fail with `:enoent`.
2019-11-22 11:20:54 +01:00
José Valim 055526057a Release v1.9.4 2019-11-05 16:29:34 +01:00
José Valim 5af52898d3 Release v1.9.3 2019-11-05 13:42:49 +01:00
José Valim 34dc2466e4 Remove warnings from rebar3 safe install 2019-11-05 12:42:33 +01:00
Bram Verburg fad48c401f Warn when using unsafe URL local installs 2019-11-05 11:53:35 +01:00
Michał Kalbarczyk 215229c28f Make release's boot scripts deterministic (#9387) 2019-11-03 18:02:23 +01:00
José Valim 1ea09243c5 Clarify escaping rules in sigils, closes #9471 2019-11-03 11:14:39 +01:00
Chris de Graaf 96c9500afd Use default_release option when name is not given (#9158) 2019-10-21 18:27:19 +02:00
Gary Rennie b6ea714339 Add :tar option for releases to create a tarball (#9290) 2019-10-16 17:37:26 +02:00
16 changed files with 366 additions and 50 deletions
+63
View File
@@ -58,6 +58,69 @@ Another important change related to configuration is that `mix new` will no long
There are many other enhancements. The Elixir CLI got a handful of new options in order to best support releases. `Logger` now computes its sync/async/discard thresholds in a decentralized fashion, reducing contention. `EEx` templates support more complex expressions than before. Finally, there is a new `~U` sigil for working with UTC DateTimes as well as new functions in the `File`, `Registry`, and `System` modules.
## v1.9.4 (2019-11-05)
### 1. Bug fixes
#### Mix
* [mix local.hex] Remove invalid deprecation warning on `mix local.hex` command
## v1.9.3 (2019-11-05)
Note this release deprecates the use of URLs on `mix archive.install`, `mix escript.install`, and `mix local.rebar`. Support for passing URLs to said commands will be fully removed on Elixir v1.10, as they are unsafe. Thanks to Bram Verburg for the report and for providing a fix.
The alternative is straight-forward: you can simply download the artifact via the command line and then invoke the command with a file system path. For example, instead of:
$ mix archive.install https://example.org/installer.ez
You can execute on Unix (Linux, MacOS X):
$ wget https://example.org/installer.ez
$ mix archive.install installer.ez
or
$ curl -o installer.ez https://example.org/installer.ez
$ mix archive.install installer.ez
On Windows (Win7 or later):
> powershell -Command "Invoke-WebRequest https://example.org/installer.ez -OutFile installer.ez"
> mix archive.install installer.ez
or
> powershell -Command "(New-Object Net.WebClient).DownloadFile('https://example.org/installer.ez', 'installer.ez')"
> mix archive.install installer.ez
Note that, if you are a library author, consider providing installable escripts and archives through Hex, such as Phoenix:
$ mix archive.install hex phx_new
Installations through Hex are always safe and they come with version management and all other benefits from Hex too.
### 1. Enhancements
#### Mix
* [mix release] Add :tar option for releases to create a tarball
### 2. Bug fixes
#### Mix
* [mix release] Use `default_release` option when name is not given
* [mix release] Make release's boot script contents deterministic
### 3. Deprecations
#### Mix
* [mix archive.install] Warn when installing from URI
* [mix escript.install] Warn when installing from URI
* [mix local.rebar] Warn when installing from URI
## v1.9.2 (2019-10-12)
### 1. Enhancements
+1 -1
View File
@@ -1 +1 @@
1.9.2
1.9.4
+21 -9
View File
@@ -4884,16 +4884,24 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~S` for strings.
It simply returns a string without escaping characters and without
interpolations.
It returns a string without interpolations and without escape
characters, except for the escaping of the closing sigil character
itself.
## Examples
iex> ~S(foo)
"foo"
iex> ~S(f#{o}o)
"f\#{o}o"
iex> ~S(\o/)
"\\o/"
However, if you want to re-use the sigil character itself on
the string, you need to escape it:
iex> ~S((\))
"()"
"""
defmacro sigil_S(term, modifiers)
@@ -4930,8 +4938,9 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~C` for charlists.
It simply returns a charlist without escaping characters and without
interpolations.
It returns a charlist without interpolations and without escape
characters, except for the escaping of the closing sigil character
itself.
## Examples
@@ -5011,8 +5020,10 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~R` for regular expressions.
It returns a regular expression pattern without escaping
nor interpreting interpolations.
It returns a regular expression pattern without interpolations and
without escape characters. Note it still supports escape of Regex
tokens (such as escaping `+` or `?`) and it also requires you to
escape the closing sigil character itself if it appears on the Regex.
More information on regexes can be found in the `Regex` module.
@@ -5175,8 +5186,9 @@ defmodule Kernel do
@doc ~S"""
Handles the sigil `~W` for list of words.
It returns a list of "words" split by whitespace without escaping nor
interpreting interpolations.
It returns a list of "words" split by whitespace without interpolations
and without escape characters, except for the escaping of the closing
sigil character itself.
## Modifiers
+7 -2
View File
@@ -95,6 +95,8 @@ defmodule Path do
absname(absname_join(name), cwd)
end
@slash [?/, ?\\]
# Joins a list
defp absname_join([name1, name2 | rest]), do: absname_join([absname_join(name1, name2) | rest])
@@ -110,6 +112,11 @@ defmodule Path do
do_absname_join(rest, relativename, [?:, uc_letter + ?a - ?A], :win32)
end
defp do_absname_join(<<c1, c2, rest::binary>>, relativename, [], :win32)
when c1 in @slash and c2 in @slash do
do_absname_join(rest, relativename, '//', :win32)
end
defp do_absname_join(<<?\\, rest::binary>>, relativename, result, :win32),
do: do_absname_join(<<?/, rest::binary>>, relativename, result, :win32)
@@ -254,8 +261,6 @@ defmodule Path do
defp unix_pathtype([list | rest]) when is_list(list), do: unix_pathtype(list ++ rest)
defp unix_pathtype(relative), do: {:relative, relative}
@slash [?/, ?\\]
defp win32_pathtype([list | rest]) when is_list(list), do: win32_pathtype(list ++ rest)
defp win32_pathtype([char, list | rest]) when is_list(list),
+12
View File
@@ -39,6 +39,13 @@ defmodule PathTest do
describe "Windows" do
@describetag :windows
test "absname/1" do
assert Path.absname("//host/path") == "//host/path"
assert Path.absname("\\\\host\\path") == "//host/path"
assert Path.absname("\\/host\\path") == "//host/path"
assert Path.absname("/\\host\\path") == "//host/path"
end
test "relative/1" do
assert Path.relative("C:/usr/local/bin") == "usr/local/bin"
assert Path.relative("C:\\usr\\local\\bin") == "usr\\local\\bin"
@@ -67,6 +74,11 @@ defmodule PathTest do
assert Path.type("/usr/local/bin") == :volumerelative
assert Path.type('usr/local/bin') == :relative
assert Path.type("../usr/local/bin") == :relative
assert Path.type("//host/path") == :absolute
assert Path.type("\\\\host\\path") == :absolute
assert Path.type("/\\host\\path") == :absolute
assert Path.type("\\/host\\path") == :absolute
end
test "split/1" do
+2 -2
View File
@@ -107,7 +107,7 @@ defmodule Mix.Local.Installer do
module.install(basename, binary, previous_files)
:badpath ->
Mix.raise("Expected #{inspect(src)} to be a URL or a local file path")
Mix.raise("Expected #{inspect(src)} to be a local file path")
{:local, message} ->
Mix.raise(message)
@@ -164,7 +164,7 @@ defmodule Mix.Local.Installer do
cond do
local_path?(url_or_path) -> {:local, url_or_path}
file_url?(url_or_path) -> {:url, url_or_path}
true -> {:error, "Expected #{inspect(url_or_path)} to be a URL or a local file path"}
true -> {:error, "Expected #{inspect(url_or_path)} to be a local file path"}
end
end
+30 -11
View File
@@ -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 [
@@ -177,10 +178,16 @@ defmodule Mix.Release do
{name, opts}
[_ | _] ->
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"
)
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
@@ -311,10 +318,12 @@ defmodule Mix.Release do
defp build_start_boot(all_apps, specified_apps) do
specified_apps ++
for(
{app, props} <- all_apps,
not List.keymember?(specified_apps, app, 0),
do: {app, default_mode(props)}
Enum.sort(
for(
{app, props} <- all_apps,
not List.keymember?(specified_apps, app, 0),
do: {app, default_mode(props)}
)
)
end
@@ -329,12 +338,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)}
""")
@@ -344,6 +355,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
+20 -13
View File
@@ -12,7 +12,7 @@ defmodule Mix.Tasks.Archive.Install do
mix do archive.build, archive.install
If an argument is provided, it should be a local path or a URL to a
If an argument is provided, it should be a local path to a
prebuilt archive, a Git repository, a GitHub repository, or a Hex
package.
@@ -35,13 +35,12 @@ defmodule Mix.Tasks.Archive.Install do
mix some_task
Note that installing via Git, GitHub, or Hex fetches the source
of the archive and builds it, while using a URL or a local path
fetches a pre-built archive.
of the archive and builds it, while using local path uses a pre-built archive.
## Command line options
* `--sha512` - checks the archive matches the given SHA-512 checksum. Only
applies to installations via URL or local path
applies to installations via a local path
* `--force` - forces installation without a shell prompt; primarily
intended for automation in build systems like Make
@@ -55,9 +54,6 @@ defmodule Mix.Tasks.Archive.Install do
* `--organization` - specifies an organization to use if fetching the package
from a private Hex repository
* `--timeout` - sets a request timeout in milliseconds for fetching
archives from URLs. Default is 60 seconds
"""
@behaviour Mix.Local.Installer
@@ -77,17 +73,28 @@ defmodule Mix.Tasks.Archive.Install do
end
@impl true
def check_install_spec({local_or_url, path_or_url} = _install_spec, _opts)
when local_or_url in [:local, :url] do
if Path.extname(path_or_url) == ".ez" do
:ok
else
{:error, "Expected a local file path or a file URL ending in \".ez\"."}
def check_install_spec({:local, path} = _install_spec, _opts) do
check_extname(path)
end
def check_install_spec({:url, url} = _install_spec, opts) do
unless opts[:sha512] do
Mix.Utils.warn_install_over_http_deprecated("archive.install", url)
end
check_extname(url)
end
def check_install_spec(_, _), do: :ok
defp check_extname(path_or_url) do
if Path.extname(path_or_url) == ".ez" do
:ok
else
{:error, "Expected a local file path ending in \".ez\"."}
end
end
@impl true
def find_previous_versions(src) do
app =
+10 -6
View File
@@ -12,12 +12,11 @@ defmodule Mix.Tasks.Escript.Install do
mix do escript.build, escript.install
If an argument is provided, it should be a local path or a URL to a prebuilt escript,
If an argument is provided, it should be a local path to a prebuilt escript,
a Git repository, a GitHub repository, or a Hex package.
mix escript.install escript
mix escript.install path/to/escript
mix escript.install https://example.com/my_escript
mix escript.install git https://path/to/git/repo
mix escript.install git https://path/to/git/repo branch git_branch
mix escript.install git https://path/to/git/repo tag git_tag
@@ -40,7 +39,7 @@ defmodule Mix.Tasks.Escript.Install do
## Command line options
* `--sha512` - checks the escript matches the given SHA-512 checksum. Only
applies to installations via URL or local path
applies to installations via a local path
* `--force` - forces installation without a shell prompt; primarily
intended for automation in build systems like Make
@@ -54,9 +53,6 @@ defmodule Mix.Tasks.Escript.Install do
* `--organization` - specifies an organization to use if fetching the package
from a private Hex repository
* `--timeout` - sets a request timeout in milliseconds for fetching
archives from URLs. Default is 60 seconds
"""
@behaviour Mix.Local.Installer
@@ -79,6 +75,14 @@ defmodule Mix.Tasks.Escript.Install do
end
@impl true
def check_install_spec({:url, url} = _install_spec, opts) do
unless opts[:sha512] do
Mix.Utils.warn_install_over_http_deprecated("escript.install", url)
end
:ok
end
def check_install_spec(_, _), do: :ok
@impl true
+42 -3
View File
@@ -25,9 +25,9 @@ defmodule Mix.Tasks.Local.Rebar do
## Command line options
* `rebar PATH` - specifies a path or URL for `rebar`
* `rebar PATH` - specifies a path for `rebar`
* `rebar3 PATH` - specifies a path or URL for `rebar3`
* `rebar3 PATH` - specifies a path for `rebar3`
* `--sha512` - checks the archive matches the given SHA-512 checksum
@@ -48,9 +48,11 @@ defmodule Mix.Tasks.Local.Rebar do
case argv do
["rebar", path | _] ->
if file_url?(path), do: warn_install_over_http_deprecated(:rebar, path)
install_from_path(:rebar, path, opts)
["rebar3", path | _] ->
if file_url?(path), do: warn_install_over_http_deprecated(:rebar3, path)
install_from_path(:rebar3, path, opts)
[] ->
@@ -77,7 +79,7 @@ defmodule Mix.Tasks.Local.Rebar do
Mix.shell().info([:green, "* creating ", :reset, Path.relative_to_cwd(local)])
:badpath ->
Mix.raise("Expected #{inspect(path)} to be a URL or a local file path")
Mix.raise("Expected #{inspect(path)} to be a local file path")
{:local, message} ->
Mix.raise(message)
@@ -100,6 +102,10 @@ defmodule Mix.Tasks.Local.Rebar do
true
end
defp file_url?(url_or_path) do
URI.parse(url_or_path).scheme in ["http", "https"]
end
defp install_from_s3(manager, list_url, escript_url, opts) do
hex_mirror = Mix.Hex.mirror()
list_url = hex_mirror <> list_url
@@ -114,4 +120,37 @@ defmodule Mix.Tasks.Local.Rebar do
install_from_path(manager, url, Keyword.put(opts, :sha512, sha512))
end
defp warn_install_over_http_deprecated(manager, url) do
shell = Mix.shell()
basename = Path.basename(url)
shell.error("""
Warning: the use of HTTP/HTTPS URLs with `mix local.rebar` is deprecated")
Run `mix help local.rebar` for details on installing #{manager} from Hex's CDN.
Alternatively you can fetch the file using an external HTTP client and then
install it locally:
Unix (Linux, MacOS X):
$ wget #{url}
$ mix local.rebar #{manager} #{basename}
or
$ curl -o #{basename} #{url}
$ mix local.rebar #{manager} #{basename}
Windows (Win7 or later):
> powershell -Command "Invoke-WebRequest #{url} -OutFile #{basename}"
> mix local.rebar #{manager} #{basename}
or
> powershell -Command "(New-Object Net.WebClient).DownloadFile('#{url}', '#{basename}')"
> mix local.rebar #{manager} #{basename}
""")
end
end
+37 -1
View File
@@ -491,7 +491,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
@@ -953,6 +955,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
@@ -1006,6 +1012,36 @@ 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 =
dirs
|> Enum.filter(&File.exists?(Path.join(release.path, &1)))
|> Enum.map(&{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
+34
View File
@@ -676,4 +676,38 @@ defmodule Mix.Utils do
[proxy_auth: {user, pass}]
end
def warn_install_over_http_deprecated(task_name, url) do
basename = Path.basename(url)
shell = Mix.shell()
shell.error("""
Warning: the use of HTTP/HTTPS URLs with `mix #{task_name}` is deprecated
Run `mix help #{task_name}` for details on the available alternatives, using
hex, git or github. Alternatively you can fetch the file using an external HTTP
client and then install it locally:
Unix (Linux, MacOS X):
$ wget #{url}
$ mix #{task_name} #{basename}
or
$ curl -o #{basename} #{url}
$ mix #{task_name} #{basename}
Windows (Win7 or later):
> powershell -Command "Invoke-WebRequest #{url} -OutFile #{basename}"
> mix #{task_name} #{basename}
or
> powershell -Command "(New-Object Net.WebClient).DownloadFile('#{url}', '#{basename}')"
> mix #{task_name} #{basename}
""")
end
end
+9 -1
View File
@@ -95,7 +95,7 @@ defmodule Mix.ReleaseTest do
test "uses chosen release via the default_release" do
release =
from_config!(
:bar,
nil,
config(
default_release: :bar,
releases: [foo: [version: "0.2.0"], bar: [version: "0.3.0"]]
@@ -168,6 +168,14 @@ defmodule Mix.ReleaseTest do
~r"The :steps option must contain the atom :assemble once",
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
+3 -1
View File
@@ -82,6 +82,8 @@ defmodule Mix.Tasks.ArchiveTest do
# Try to override it with URL
send(self(), {:mix_shell_input, :yes?, false})
Mix.Tasks.Archive.Install.run(["https://example.com/archive-0.1.0?hello.ez"])
assert_received {:mix_shell, :error, ["Warning: the use of HTTP/HTTPS URLs" <> _]}
assert_received {:mix_shell, :yes?, ["Found existing entry: " <> _]}
# Loading the archive should emit warning again
@@ -116,7 +118,7 @@ defmodule Mix.Tasks.ArchiveTest do
end
test "archive install missing file" do
message = ~r[Expected "./unlikely-to-exist-0.1.0.ez" to be a URL or a local file path]
message = ~r[Expected "./unlikely-to-exist-0.1.0.ez" to be a local file path]
assert_raise Mix.Error, message, fn ->
Mix.Tasks.Archive.Install.run(["./unlikely-to-exist-0.1.0.ez"])
+7
View File
@@ -242,6 +242,13 @@ defmodule Mix.Tasks.EscriptTest do
assert_received {:mix_shell, :info, ["* escript_test"]}
refute_received {:mix_shell, :info, ["* escript_test.bat"]}
# Try to override it with URL
send(self(), {:mix_shell_input, :yes?, false})
Mix.Tasks.Escript.Install.run(["https://example.com/escript_test"])
assert_received {:mix_shell, :error, ["Warning: the use of HTTP/HTTPS URLs" <> _]}
assert_received {:mix_shell, :yes?, ["Found existing entry: " <> _]}
# check uninstall confirmation
send(self(), {:mix_shell_input, :yes?, false})
Mix.Tasks.Escript.Uninstall.run(["escript_test"])
+68
View File
@@ -36,6 +36,74 @@ 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
assert Enum.any?(files, &(&1 =~ "erts"))
assert Enum.any?(files, &(&1 =~ "stdlib"))
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 "tar without ERTS" do
in_fixture("release_test", fn ->
config = [releases: [demo: [include_erts: false, steps: [:assemble, :tar]]]]
Mix.Project.in_project(:release_test, ".", config, fn _ ->
root = Path.absname("_build/#{Mix.env()}/rel/demo")
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)
assert "bin/demo" in files
refute Enum.any?(files, &(&1 =~ "erts"))
refute Enum.any?(files, &(&1 =~ "stdlib"))
end)
end)
end
test "steps" do
in_fixture("release_test", fn ->
last_step = fn release ->