From 80984be08718501bf6a0b33278e37b57bc62be10 Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Sun, 2 Aug 2026 15:09:01 +0200 Subject: [PATCH 1/2] Ensure no double newlines in files generated by "mix new" Add assertions to Mix.Tasks.NewTest to refute consecutive empty lines ("\n\n\n") in generated README.md and .gitignore files for both standard and umbrella projects, preventing accidental regression of template formatting issues (such as fixed in 7a7d7d9565e8d2177573d2d43efbf99fd7795791). --- lib/mix/test/mix/tasks/new_test.exs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/mix/test/mix/tasks/new_test.exs b/lib/mix/test/mix/tasks/new_test.exs index 5677735b73e..8a2c95fefbb 100644 --- a/lib/mix/test/mix/tasks/new_test.exs +++ b/lib/mix/test/mix/tasks/new_test.exs @@ -20,11 +20,13 @@ defmodule Mix.Tasks.NewTest do assert file =~ "# HelloWorld\n" assert String.ends_with?(file, "\n") refute String.ends_with?(file, "\n\n") + refute file =~ "\n\n\n" end) assert_file("hello_world/.gitignore", fn file -> assert String.ends_with?(file, "\n") refute String.ends_with?(file, "\n\n") + refute file =~ "\n\n\n" end) assert_file("hello_world/lib/hello_world.ex", ~r/defmodule HelloWorld do/) @@ -130,11 +132,13 @@ defmodule Mix.Tasks.NewTest do assert file =~ "# HelloWorld\n" assert String.ends_with?(file, "\n") refute String.ends_with?(file, "\n\n") + refute file =~ "\n\n\n" end) assert_file("hello_world/.gitignore", fn file -> assert String.ends_with?(file, "\n") refute String.ends_with?(file, "\n\n") + refute file =~ "\n\n\n" end) assert_received {:mix_shell, :info, ["* creating mix.exs"]} From f35592ae695d56b18d7ec6f236a9929f089abfbc Mon Sep 17 00:00:00 2001 From: Rodolfo Carvalho Date: Sun, 2 Aug 2026 23:52:52 +0200 Subject: [PATCH 2/2] Enforce rules on every generated file Replicate the approach from phoenixframework/phoenix#6781, since clearly the Phoenix installer and `mix new` share the `assert_file/1` and `assert_file/2` helpers. All generated files are candidates for these two rules: 1. Every file must end with a single trailing newline. 2. No file may contain consecutive blank lines. Instead of updating lots of tests (which would cause a lot of churn and be a future maintenance burden, easy to miss in new tests), update the helpers to enforce these rules on every file. Exceptions can be easily added in the future if needed. --- lib/mix/test/mix/tasks/new_test.exs | 41 +++++++++++------------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/lib/mix/test/mix/tasks/new_test.exs b/lib/mix/test/mix/tasks/new_test.exs index 8a2c95fefbb..9f411d0e860 100644 --- a/lib/mix/test/mix/tasks/new_test.exs +++ b/lib/mix/test/mix/tasks/new_test.exs @@ -16,18 +16,8 @@ defmodule Mix.Tasks.NewTest do assert file =~ "version: \"0.1.0\"" end) - assert_file("hello_world/README.md", fn file -> - assert file =~ "# HelloWorld\n" - assert String.ends_with?(file, "\n") - refute String.ends_with?(file, "\n\n") - refute file =~ "\n\n\n" - end) - - assert_file("hello_world/.gitignore", fn file -> - assert String.ends_with?(file, "\n") - refute String.ends_with?(file, "\n\n") - refute file =~ "\n\n\n" - end) + assert_file("hello_world/README.md", ~r/# HelloWorld\n/) + assert_file("hello_world/.gitignore") assert_file("hello_world/lib/hello_world.ex", ~r/defmodule HelloWorld do/) assert_file("hello_world/test/test_helper.exs", ~r/ExUnit.start()/) @@ -128,18 +118,8 @@ defmodule Mix.Tasks.NewTest do assert file =~ "apps_path: \"apps\"" end) - assert_file("hello_world/README.md", fn file -> - assert file =~ "# HelloWorld\n" - assert String.ends_with?(file, "\n") - refute String.ends_with?(file, "\n\n") - refute file =~ "\n\n\n" - end) - - assert_file("hello_world/.gitignore", fn file -> - assert String.ends_with?(file, "\n") - refute String.ends_with?(file, "\n\n") - refute file =~ "\n\n\n" - end) + assert_file("hello_world/README.md", ~r/# HelloWorld\n/) + assert_file("hello_world/.gitignore") assert_received {:mix_shell, :info, ["* creating mix.exs"]} @@ -264,6 +244,15 @@ defmodule Mix.Tasks.NewTest do defp assert_file(file) do assert File.regular?(file), "Expected #{file} to exist, but does not" + content = File.read!(file) + + # \S\n\z matches non-whitespace followed by a single newline at EOF + assert content == "" or content =~ ~r/\S\n\z/, + "Expected #{file} to end with a single trailing newline" + + refute content =~ "\n\n\n", "Expected #{file} to not contain consecutive blank lines" + + content end defp assert_file(file, match) do @@ -272,8 +261,8 @@ defmodule Mix.Tasks.NewTest do assert_file(file, &assert(&1 =~ match)) is_function(match, 1) -> - assert_file(file) - match.(File.read!(file)) + content = assert_file(file) + match.(content) end end end