Skip to content

Commit b09914b

Browse files
authored
Make .app files deterministic in releases (#13763)
When copying .app files to a release, remove references to `:compile_mtime` so that their contents don't change between builds.
1 parent 699ba28 commit b09914b

File tree

2 files changed

+41
-16
lines changed

2 files changed

+41
-16
lines changed

lib/mix/lib/mix/release.ex

+26-4
Original file line numberDiff line numberDiff line change
@@ -851,10 +851,13 @@ defmodule Mix.Release do
851851
source_file = Path.join(source, file)
852852
target_file = Path.join(target, file)
853853

854-
with true <- is_list(strip_options) and String.ends_with?(file, ".beam"),
855-
{:ok, binary} <- strip_beam(File.read!(source_file), strip_options) do
856-
File.write!(target_file, binary)
857-
else
854+
case Path.extname(file) do
855+
".beam" ->
856+
process_beam_file(source_file, target_file, strip_options)
857+
858+
".app" ->
859+
process_app_file(source_file, target_file)
860+
858861
_ ->
859862
# Use File.cp!/3 to preserve file mode for any executables stored
860863
# in the ebin directory.
@@ -868,6 +871,25 @@ defmodule Mix.Release do
868871
end
869872
end
870873

874+
defp process_beam_file(source_file, target_file, strip_options) do
875+
with true <- is_list(strip_options),
876+
{:ok, binary} <- strip_beam(File.read!(source_file), strip_options) do
877+
File.write!(target_file, binary)
878+
else
879+
_ -> File.cp!(source_file, target_file)
880+
end
881+
end
882+
883+
defp process_app_file(source_file, target_file) do
884+
with {:ok, [{:application, app, info}]} <- :file.consult(source_file) do
885+
# Remove :config_mtime so that .app files are deterministic between builds
886+
new_info = Keyword.delete(info, :config_mtime)
887+
File.write!(target_file, :io_lib.format("~tp.~n", [{:application, app, new_info}]))
888+
else
889+
_ -> File.cp!(source_file, target_file)
890+
end
891+
end
892+
871893
@doc """
872894
Strips a beam file for a release.
873895

lib/mix/test/mix/release_test.exs

+15-12
Original file line numberDiff line numberDiff line change
@@ -686,19 +686,13 @@ defmodule Mix.ReleaseTest do
686686
test "copies and strips beams" do
687687
assert copy_ebin(release([]), @eex_ebin, tmp_path("eex_ebin"))
688688

689-
assert size!(Path.join(@eex_ebin, "eex.app")) ==
690-
size!(tmp_path("eex_ebin/eex.app"))
691-
692689
assert size!(Path.join(@eex_ebin, "Elixir.EEx.beam")) >
693690
size!(tmp_path("eex_ebin/Elixir.EEx.beam"))
694691
end
695692

696693
test "copies without stripping beams" do
697694
assert copy_ebin(release(strip_beams: false), @eex_ebin, tmp_path("eex_ebin"))
698695

699-
assert size!(Path.join(@eex_ebin, "eex.app")) ==
700-
size!(tmp_path("eex_ebin/eex.app"))
701-
702696
assert size!(Path.join(@eex_ebin, "Elixir.EEx.beam")) ==
703697
size!(tmp_path("eex_ebin/Elixir.EEx.beam"))
704698
end
@@ -722,6 +716,13 @@ defmodule Mix.ReleaseTest do
722716

723717
assert mode!(source_so_path) == mode!(tmp_path("mix_release/libtest_nif.so"))
724718
end
719+
720+
test "removes config_mtime from app files" do
721+
assert copy_ebin(release([]), @eex_ebin, tmp_path("eex_ebin"))
722+
723+
{:ok, [{:application, :eex, info}]} = :file.consult(tmp_path("eex_ebin/eex.app"))
724+
refute Keyword.get(info, :config_mtime)
725+
end
725726
end
726727

727728
describe "copy_app/2" do
@@ -730,19 +731,13 @@ defmodule Mix.ReleaseTest do
730731
test "copies and strips beams" do
731732
assert copy_app(release(applications: [eex: :permanent]), :eex)
732733

733-
assert size!(Path.join(@eex_ebin, "eex.app")) ==
734-
size!(Path.join(@release_lib, "eex-#{@elixir_version}/ebin/eex.app"))
735-
736734
assert size!(Path.join(@eex_ebin, "Elixir.EEx.beam")) >
737735
size!(Path.join(@release_lib, "eex-#{@elixir_version}/ebin/Elixir.EEx.beam"))
738736
end
739737

740738
test "copies without stripping beams" do
741739
assert copy_app(release(strip_beams: false, applications: [eex: :permanent]), :eex)
742740

743-
assert size!(Path.join(@eex_ebin, "eex.app")) ==
744-
size!(Path.join(@release_lib, "eex-#{@elixir_version}/ebin/eex.app"))
745-
746741
assert size!(Path.join(@eex_ebin, "Elixir.EEx.beam")) ==
747742
size!(Path.join(@release_lib, "eex-#{@elixir_version}/ebin/Elixir.EEx.beam"))
748743
end
@@ -760,6 +755,14 @@ defmodule Mix.ReleaseTest do
760755
refute File.exists?(Path.join(@release_lib, "runtime_tools-#{@runtime_tools_version}/ebin"))
761756
refute File.exists?(Path.join(@release_lib, "runtime_tools-#{@runtime_tools_version}/priv"))
762757
end
758+
759+
test "removes config_mtime from app files" do
760+
assert copy_app(release(strip_beams: false, applications: [eex: :permanent]), :eex)
761+
762+
eex_app_path = Path.join(@release_lib, "eex-#{@elixir_version}/ebin/eex.app")
763+
{:ok, [{:application, :eex, info}]} = :file.consult(eex_app_path)
764+
refute Keyword.get(info, :config_mtime)
765+
end
763766
end
764767

765768
describe "strip_beam/1" do

0 commit comments

Comments
 (0)