From d34f0be98c52018ada65c7f11b470bd14b4bf20c Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Fri, 21 Mar 2025 22:13:58 -0700 Subject: [PATCH 01/10] =?UTF-8?q?=F0=9F=91=B7=20Update=20CI=20matrix=20to?= =?UTF-8?q?=20build=20on=20elixir=20main?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9fb4ded..953aa2b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,8 @@ jobs: otp: '27' - elixir: '1.18' otp: '27' + - elixir: main + otp: '27' steps: - name: Set up Elixir id: setup From bb36f9f7d695141e56df4c50daecefd8d06a7582 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 22 Mar 2025 19:08:22 -0700 Subject: [PATCH 02/10] =?UTF-8?q?=F0=9F=91=BD=20Use=20`+`=20as=20task=20se?= =?UTF-8?q?parator=20with=20`mix=20do`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `+` is now [the preferred separator](https://hexdocs.pm/mix/1.18.3/Mix.Tasks.Do.html) for `mix do` and has been supported since Elixir 1.14. Given that it has better support on Windows than the older `,`, we'll officially drop support for Elixir 1.13 and earlier and use the `+` separator. --- lib/mix_test_interactive/port_runner.ex | 2 +- mix.exs | 2 +- test/mix_test_interactive/port_runner_test.exs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mix_test_interactive/port_runner.ex b/lib/mix_test_interactive/port_runner.ex index 7707de2..0c5bcfd 100644 --- a/lib/mix_test_interactive/port_runner.ex +++ b/lib/mix_test_interactive/port_runner.ex @@ -51,7 +51,7 @@ defmodule MixTestInteractive.PortRunner do defp enable_ansi(task) do enable_command = "Application.put_env(:elixir, :ansi_enabled, true)" - ["do", "eval", enable_command, ",", task] + ["do", "eval", enable_command, "+", task] end defp maybe_print_command(%Config{verbose?: false} = _config, _runner_program, _runner_program_args), do: :ok diff --git a/mix.exs b/mix.exs index c965071..fb065f3 100644 --- a/mix.exs +++ b/mix.exs @@ -10,7 +10,7 @@ defmodule MixTestInteractive.MixProject do deps: deps(), description: description(), docs: docs(), - elixir: "~> 1.13", + elixir: "~> 1.14", name: "mix test.interactive", package: package(), source_url: @source_url, diff --git a/test/mix_test_interactive/port_runner_test.exs b/test/mix_test_interactive/port_runner_test.exs index b9ed800..4680bd9 100644 --- a/test/mix_test_interactive/port_runner_test.exs +++ b/test/mix_test_interactive/port_runner_test.exs @@ -74,7 +74,7 @@ defmodule MixTestInteractive.PortRunnerTest do test "enables ansi output when turned on" do config = config(%{ansi_enabled?: true}) - {"mix", ["do", "eval", ansi, ",", "test"], _options} = run(config: config) + {"mix", ["do", "eval", ansi, "+", "test"], _options} = run(config: config) assert ansi =~ ~r/:ansi_enabled/ end From 2481af647af05cbdc08c7582ef5b407fa8d1cea5 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 7 Jun 2025 22:18:12 -0700 Subject: [PATCH 03/10] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Use=20Config.new=20c?= =?UTF-8?q?onsistently?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow passing in overrides to `Config.new` and merge them into the default `ansi_enabled?` flag. We now no longer rely on manually creating Config structs in the tests. --- lib/mix_test_interactive/config.ex | 15 +++++---------- .../command_line_parser_test.exs | 2 +- test/mix_test_interactive/paths_test.exs | 9 +++++---- test/mix_test_interactive/port_runner_test.exs | 18 ++++++++++-------- test/mix_test_interactive/runner_test.exs | 4 ++-- 5 files changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/mix_test_interactive/config.ex b/lib/mix_test_interactive/config.ex index f8c9f11..04a3127 100644 --- a/lib/mix_test_interactive/config.ex +++ b/lib/mix_test_interactive/config.ex @@ -36,10 +36,13 @@ defmodule MixTestInteractive.Config do end @doc false - def new do + def new(overrides \\ []) do os_type = ProcessTree.get(:os_type, default: :os.type()) - default_ansi_enabled(%__MODULE__{}, os_type) + defaults = [ansi_enabled?: not match?({:win32, _os_name}, os_type)] + attrs = Keyword.merge(defaults, overrides) + + struct!(%__MODULE__{}, attrs) end defp load(%__MODULE__{} = config, app_key, opts \\ []) do @@ -59,14 +62,6 @@ defmodule MixTestInteractive.Config do end end - defp default_ansi_enabled(%__MODULE__{} = config, {:win32, _os_name} = _os_type) do - %{config | ansi_enabled?: false} - end - - defp default_ansi_enabled(%__MODULE__{} = config, _os_type) do - %{config | ansi_enabled?: true} - end - defp parse_command({cmd, args} = command) when is_binary(cmd) and is_list(args), do: command defp parse_command(command) when is_binary(command), do: {command, []} diff --git a/test/mix_test_interactive/command_line_parser_test.exs b/test/mix_test_interactive/command_line_parser_test.exs index a7823a1..220547d 100644 --- a/test/mix_test_interactive/command_line_parser_test.exs +++ b/test/mix_test_interactive/command_line_parser_test.exs @@ -106,7 +106,7 @@ defmodule MixTestInteractive.CommandLineParserTest do test "ignores custom command arguments if command is not specified" do {:ok, %{config: config}} = CommandLineParser.parse(["--arg", "arg_with_missing_command"]) - assert config.command == %Config{}.command + assert config.command == Config.new().command end test "configures watch exclusions with --exclude" do diff --git a/test/mix_test_interactive/paths_test.exs b/test/mix_test_interactive/paths_test.exs index ebfdb97..58f4d67 100644 --- a/test/mix_test_interactive/paths_test.exs +++ b/test/mix_test_interactive/paths_test.exs @@ -41,7 +41,7 @@ defmodule MixTestInteractive.PathsTest do end test "extra extensions are watched" do - config = %Config{extra_extensions: [".ex", ".haml", ".foo", ".txt"]} + config = Config.new(extra_extensions: [".ex", ".haml", ".foo", ".txt"]) assert watching?("foo.ex", config) assert watching?("index.html.haml", config) assert watching?("my.foo", config) @@ -71,14 +71,15 @@ defmodule MixTestInteractive.PathsTest do end test "migrations_.* files should be excluded watched" do - refute watching?("migrations_files/foo.exs", %Config{exclude: [~r/migrations_.*/]}) + refute watching?("migrations_files/foo.exs", Config.new(exclude: [~r/migrations_.*/])) end test "app.ex is not excluded by migrations_.* pattern" do - assert watching?("app.ex", %Config{exclude: [~r/migrations_.*/]}) + assert watching?("app.ex", Config.new(exclude: [~r/migrations_.*/])) end - defp watching?(path, config \\ %Config{}) do + defp watching?(path, config \\ nil) do + config = config || Config.new() Paths.watching?(path, config) end end diff --git a/test/mix_test_interactive/port_runner_test.exs b/test/mix_test_interactive/port_runner_test.exs index 4680bd9..004d993 100644 --- a/test/mix_test_interactive/port_runner_test.exs +++ b/test/mix_test_interactive/port_runner_test.exs @@ -9,8 +9,10 @@ defmodule MixTestInteractive.PortRunnerTest do @unix {:unix, :darwin} @windows {:win32, :nt} - defp config(overrides \\ %{}) do - Map.merge(%Config{ansi_enabled?: false}, overrides) + defp config(overrides \\ []) do + [ansi_enabled?: false] + |> Keyword.merge(overrides) + |> Config.new() end defp run(options \\ []) do @@ -73,7 +75,7 @@ defmodule MixTestInteractive.PortRunnerTest do end test "enables ansi output when turned on" do - config = config(%{ansi_enabled?: true}) + config = config(ansi_enabled?: true) {"mix", ["do", "eval", ansi, "+", "test"], _options} = run(config: config) assert ansi =~ ~r/:ansi_enabled/ @@ -88,22 +90,22 @@ defmodule MixTestInteractive.PortRunnerTest do end test "uses custom task" do - config = config(%{task: "custom_task"}) + config = config(task: "custom_task") assert {_command, ["custom_task"], _options} = run(config: config) end test "uses custom command with no args" do - config = config(%{command: {"custom_command", []}}) + config = config(command: {"custom_command", []}) assert {"custom_command", _args, _options} = run(config: config) end test "uses custom command with args" do - config = config(%{command: {"custom_command", ["--custom_arg"]}}) + config = config(command: {"custom_command", ["--custom_arg"]}) assert {"custom_command", ["--custom_arg", "test"], _options} = run(config: config) end test "prepends command args to test args" do - config = config(%{command: {"custom_command", ["--custom_arg"]}}) + config = config(command: {"custom_command", ["--custom_arg"]}) assert {"custom_command", ["--custom_arg", "test", "--cover"], _options} = run(args: ["--cover"], config: config) @@ -117,7 +119,7 @@ defmodule MixTestInteractive.PortRunnerTest do end test "displays command in verbose mode" do - config = config(%{verbose?: true}) + config = config(verbose?: true) {result, output} = with_io(fn -> run(config: config) end) diff --git a/test/mix_test_interactive/runner_test.exs b/test/mix_test_interactive/runner_test.exs index aef5c80..9c761d0 100644 --- a/test/mix_test_interactive/runner_test.exs +++ b/test/mix_test_interactive/runner_test.exs @@ -31,7 +31,7 @@ defmodule MixTestInteractive.RunnerTest do describe "run/1" do test "It delegates to the runner specified by the config" do - config = %Config{runner: DummyRunner} + config = Config.new(runner: DummyRunner) args = ["--cover", "--raise"] output = @@ -45,7 +45,7 @@ defmodule MixTestInteractive.RunnerTest do end test "It outputs timestamp when specified by the config" do - config = %Config{runner: DummyRunner, show_timestamp?: true} + config = Config.new(runner: DummyRunner, show_timestamp?: true) output = capture_io(fn -> From ff02992df4497d480ec96a2ca7a2fe8cbeb593c3 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 7 Jun 2025 22:20:22 -0700 Subject: [PATCH 04/10] =?UTF-8?q?=F0=9F=91=BD=20Move=20regexes=20out=20of?= =?UTF-8?q?=20compile-time=20code=20paths?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This prepares for Elixir 1.19/OTP 28 dropping support for compile-time regexes. Co-authored-by: Frank Dugan III --- .../command_line_formatter.ex | 19 ++++++++++++++----- lib/mix_test_interactive/config.ex | 4 ++-- test/mix_test_interactive/end_to_end_test.exs | 10 +++++----- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/mix_test_interactive/command_line_formatter.ex b/lib/mix_test_interactive/command_line_formatter.ex index 741c6e3..67c8159 100644 --- a/lib/mix_test_interactive/command_line_formatter.ex +++ b/lib/mix_test_interactive/command_line_formatter.ex @@ -1,15 +1,24 @@ defmodule MixTestInteractive.CommandLineFormatter do @moduledoc false - @special_chars ~r/[\s&|;<>*?()\[\]{}$`'"]/ - @whitespace ~r/\s/ - def call(command, args) do Enum.map_join([command | args], " ", &format_argument/1) end + defmacrop special_chars do + quote do + ~r/[\s&|;<>*?()\[\]{}$`'"]/ + end + end + + defmacrop whitespace do + quote do + ~r/\s/ + end + end + defp format_argument(arg) do - if arg =~ @special_chars do + if arg =~ special_chars() do quote_argument(arg) else arg @@ -19,7 +28,7 @@ defmodule MixTestInteractive.CommandLineFormatter do defp quote_argument(arg) do cond do # Prefer double quotes for arguments with only spaces or special characters - String.match?(arg, @whitespace) and not String.contains?(arg, ~s(")) -> + String.match?(arg, whitespace()) and not String.contains?(arg, ~s(")) -> ~s("#{arg}") # Use single quotes if the argument contains double quotes but no single quotes diff --git a/lib/mix_test_interactive/config.ex b/lib/mix_test_interactive/config.ex index 04a3127..d1e4b8e 100644 --- a/lib/mix_test_interactive/config.ex +++ b/lib/mix_test_interactive/config.ex @@ -10,7 +10,7 @@ defmodule MixTestInteractive.Config do field :ansi_enabled?, boolean() field :clear?, boolean(), default: false field :command, {String.t(), [String.t()]}, default: {"mix", []} - field :exclude, [Regex.t()], default: [~r/\.#/, ~r{priv/repo/migrations}] + field :exclude, [Regex.t()] field :extra_extensions, [String.t()], default: [] field :runner, module(), default: MixTestInteractive.PortRunner field :show_timestamp?, boolean(), default: false @@ -39,7 +39,7 @@ defmodule MixTestInteractive.Config do def new(overrides \\ []) do os_type = ProcessTree.get(:os_type, default: :os.type()) - defaults = [ansi_enabled?: not match?({:win32, _os_name}, os_type)] + defaults = [ansi_enabled?: not match?({:win32, _os_name}, os_type), exclude: [~r/\.#/, ~r{priv/repo/migrations}]] attrs = Keyword.merge(defaults, overrides) struct!(%__MODULE__{}, attrs) diff --git a/test/mix_test_interactive/end_to_end_test.exs b/test/mix_test_interactive/end_to_end_test.exs index d40334e..9de2a0e 100644 --- a/test/mix_test_interactive/end_to_end_test.exs +++ b/test/mix_test_interactive/end_to_end_test.exs @@ -28,15 +28,15 @@ defmodule MixTestInteractive.EndToEndTest do end end - @config %Config{runner: DummyRunner} - @settings %Settings{} - setup do {:ok, io} = StringIO.open("") Process.group_leader(self(), io) + config = Config.new(runner: DummyRunner) + settings = %Settings{} + _pid = start_supervised!({DummyRunner, self()}) - pid = start_supervised!({InteractiveMode, config: @config, name: :end_to_end, settings: @settings}) + pid = start_supervised!({InteractiveMode, config: config, name: :end_to_end, settings: settings}) %{pid: pid} end @@ -160,7 +160,7 @@ defmodule MixTestInteractive.EndToEndTest do end defp assert_ran_tests(args \\ []) do - assert_receive {@config, ^args}, 100 + assert_receive {%Config{}, ^args}, 100 end defp refute_ran_tests do From 747730dc7f0988a58dc66a928542808b868d3aff Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 7 Jun 2025 22:45:38 -0700 Subject: [PATCH 05/10] =?UTF-8?q?=F0=9F=91=BD=20Compare=20regex=20sources?= =?UTF-8?q?=20rather=20than=20full=20regexes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OTP 28 changes the internal structure of regexes so that they are no longer directly comparable. We add some undocumented helper functions to `Config` for doing the necessary comparisons and then use them. --- lib/mix_test_interactive/config.ex | 17 +++++++++++++++++ .../command_line_parser_test.exs | 8 ++++---- test/mix_test_interactive/config_test.exs | 6 +++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/lib/mix_test_interactive/config.ex b/lib/mix_test_interactive/config.ex index d1e4b8e..765e23a 100644 --- a/lib/mix_test_interactive/config.ex +++ b/lib/mix_test_interactive/config.ex @@ -45,6 +45,23 @@ defmodule MixTestInteractive.Config do struct!(%__MODULE__{}, attrs) end + @doc false + def equal?(%__MODULE__{} = left, %__MODULE__{} = right) do + %{left | exclude: nil} == %{right | exclude: nil} and exclude_matches?(left, right.exclude) + end + + @doc false + def exclude_contains?(%__MODULE__{} = config, %Regex{} = regex) do + regex.source in sources(config.exclude) + end + + @doc false + def exclude_matches?(%__MODULE__{} = config, exclude) do + sources(config.exclude) == sources(exclude) + end + + defp sources(exclude), do: Enum.map(exclude, &Regex.source/1) + defp load(%__MODULE__{} = config, app_key, opts \\ []) do config_key = Keyword.get(opts, :rename, app_key) transform = Keyword.get(opts, :transform, & &1) diff --git a/test/mix_test_interactive/command_line_parser_test.exs b/test/mix_test_interactive/command_line_parser_test.exs index 220547d..0fdc507 100644 --- a/test/mix_test_interactive/command_line_parser_test.exs +++ b/test/mix_test_interactive/command_line_parser_test.exs @@ -62,7 +62,7 @@ defmodule MixTestInteractive.CommandLineParserTest do describe "mix test.interactive options" do test "retains original defaults when no options" do {:ok, %{config: config}} = CommandLineParser.parse([]) - assert config == Config.new() + assert Config.equal?(config, Config.new()) end test "sets ansi_enabled? flag with --ansi-enabled" do @@ -111,12 +111,12 @@ defmodule MixTestInteractive.CommandLineParserTest do test "configures watch exclusions with --exclude" do {:ok, %{config: config}} = CommandLineParser.parse(["--exclude", "~$"]) - assert config.exclude == [~r/~$/] + assert Config.exclude_matches?(config, [~r/~$/]) end test "configures multiple watch exclusions with repeated --exclude options" do {:ok, %{config: config}} = CommandLineParser.parse(["--exclude", "~$", "--exclude", "\.secret\.exs"]) - assert config.exclude == [~r/~$/, ~r/.secret.exs/] + assert Config.exclude_matches?(config, [~r/~$/, ~r/.secret.exs/]) end test "fails if watch exclusion is an invalid Regex" do @@ -342,7 +342,7 @@ defmodule MixTestInteractive.CommandLineParserTest do {:ok, %{config: config, settings: settings}} = CommandLineParser.parse(["--exclude", "~$", "--", "--exclude", "integration"]) - assert config.exclude == [~r/~$/] + assert Config.exclude_matches?(config, [~r/~$/]) assert settings.excludes == ["integration"] end diff --git a/test/mix_test_interactive/config_test.exs b/test/mix_test_interactive/config_test.exs index 9aaa971..e55a758 100644 --- a/test/mix_test_interactive/config_test.exs +++ b/test/mix_test_interactive/config_test.exs @@ -61,18 +61,18 @@ defmodule MixTestInteractive.ConfigTest do test "takes :exclude from the env" do Process.put(:exclude, [~r/migration_.*/]) config = Config.load_from_environment() - assert config.exclude == [~r/migration_.*/] + assert Config.exclude_matches?(config, [~r/migration_.*/]) end test ":exclude contains common editor temp/swap files by default" do config = Config.load_from_environment() # Emacs lock symlink - assert ~r/\.#/ in config.exclude + assert Config.exclude_contains?(config, ~r/\.#/) end test "excludes default Phoenix migrations directory by default" do config = Config.load_from_environment() - assert ~r{priv/repo/migrations} in config.exclude + assert Config.exclude_contains?(config, ~r{priv/repo/migrations}) end test "takes :extra_extensions from the env" do From 00b4c1df67ee020dc228b1913f7f3efc4021063b Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 7 Jun 2025 22:52:37 -0700 Subject: [PATCH 06/10] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20ex=5Fdoc?= =?UTF-8?q?=20to=20latest=20version?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Includes OTP 28 compatibility. --- mix.exs | 2 +- mix.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mix.exs b/mix.exs index fb065f3..a5c7113 100644 --- a/mix.exs +++ b/mix.exs @@ -32,7 +32,7 @@ defmodule MixTestInteractive.MixProject do defp deps do [ - {:ex_doc, "~> 0.35.1", only: :dev, runtime: false}, + {:ex_doc, "~> 0.38.2", only: :dev, runtime: false}, {:file_system, "~> 0.2 or ~> 1.0"}, {:process_tree, "~> 0.1.3 or ~> 0.2.0"}, {:styler, "~> 1.2", only: [:dev, :test], runtime: false}, diff --git a/mix.lock b/mix.lock index 51dbe33..4431b70 100644 --- a/mix.lock +++ b/mix.lock @@ -1,11 +1,11 @@ %{ - "earmark_parser": {:hex, :earmark_parser, "1.4.41", "ab34711c9dc6212dda44fcd20ecb87ac3f3fce6f0ca2f28d4a00e4154f8cd599", [:mix], [], "hexpm", "a81a04c7e34b6617c2792e291b5a2e57ab316365c2644ddc553bb9ed863ebefa"}, - "ex_doc": {:hex, :ex_doc, "0.35.1", "de804c590d3df2d9d5b8aec77d758b00c814b356119b3d4455e4b8a8687aecaf", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "2121c6402c8d44b05622677b761371a759143b958c6c19f6558ff64d0aed40df"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.44", "f20830dd6b5c77afe2b063777ddbbff09f9759396500cdbe7523efd58d7a339c", [:mix], [], "hexpm", "4778ac752b4701a5599215f7030989c989ffdc4f6df457c5f36938cc2d2a2750"}, + "ex_doc": {:hex, :ex_doc, "0.38.2", "504d25eef296b4dec3b8e33e810bc8b5344d565998cd83914ffe1b8503737c02", [:mix], [{:earmark_parser, "~> 1.4.44", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "732f2d972e42c116a70802f9898c51b54916e542cc50968ac6980512ec90f42b"}, "file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"}, "makeup": {:hex, :makeup, "1.2.1", "e90ac1c65589ef354378def3ba19d401e739ee7ee06fb47f94c687016e3713d1", [:mix], [{:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "d36484867b0bae0fea568d10131197a4c2e47056a6fbe84922bf6ba71c8d17ce"}, "makeup_elixir": {:hex, :makeup_elixir, "1.0.1", "e928a4f984e795e41e3abd27bfc09f51db16ab8ba1aebdba2b3a575437efafc2", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "7284900d412a3e5cfd97fdaed4f5ed389b8f2b4cb49efc0eb3bd10e2febf9507"}, - "makeup_erlang": {:hex, :makeup_erlang, "1.0.1", "c7f58c120b2b5aa5fd80d540a89fdf866ed42f1f3994e4fe189abebeab610839", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "8a89a1eeccc2d798d6ea15496a6e4870b75e014d1af514b1b71fa33134f57814"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "process_tree": {:hex, :process_tree, "0.2.0", "a934111ff00c1b696731edf35ecc6a4724105e045efa95a1977cac21ebad62cd", [:mix], [], "hexpm", "92901d3e9d2f40f4e09c7774a97ed0d1fac5d3541c62552df8346dfa0f9ee19b"}, "styler": {:hex, :styler, "1.2.1", "28f9e3d4b065c22575c56b8ae03d05188add1b21bec5ae664fc1551e2dfcc41b", [:mix], [], "hexpm", "71dc33980e530d21ca54db9c2075e646faa6e7b744a9d4a3dfb0ff01f56595f0"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, From 4d8d65932dba1ca7da87cd84000d5554e7823dcb Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 7 Jun 2025 22:58:41 -0700 Subject: [PATCH 07/10] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Use=20latest=20elixi?= =?UTF-8?q?r/OTP=20versions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runs v1.19/OTP 28 locally and in CI. Also updates the `main` build to use OTP 28. --- .github/workflows/ci.yml | 6 ++++-- .tool-versions | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 953aa2b..c7f8314 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,8 +29,10 @@ jobs: otp: '27' - elixir: '1.18' otp: '27' + - elixir: '1.19' + otp: '28' - elixir: main - otp: '27' + otp: '28' steps: - name: Set up Elixir id: setup @@ -82,7 +84,7 @@ jobs: run: mix compile --warnings-as-errors - name: Check formatting - if: ${{matrix.elixir == '1.18'}} + if: ${{matrix.elixir == '1.19'}} run: mix format --check-formatted - name: Run tests diff --git a/.tool-versions b/.tool-versions index bb51a93..a2abd93 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -erlang 27.2 -elixir 1.18.1-otp-27 +erlang 28.0 +elixir 1.19-otp-28 From 9db3ad293f5d99945e0110be84d57e154c4627d2 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 7 Jun 2025 23:00:49 -0700 Subject: [PATCH 08/10] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Upgrade=20to=20lates?= =?UTF-8?q?t=20Styler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It includes some new rules to automatically fix some Elixir 1.19 deprecations. --- mix.exs | 2 +- mix.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mix.exs b/mix.exs index a5c7113..ccefff5 100644 --- a/mix.exs +++ b/mix.exs @@ -35,7 +35,7 @@ defmodule MixTestInteractive.MixProject do {:ex_doc, "~> 0.38.2", only: :dev, runtime: false}, {:file_system, "~> 0.2 or ~> 1.0"}, {:process_tree, "~> 0.1.3 or ~> 0.2.0"}, - {:styler, "~> 1.2", only: [:dev, :test], runtime: false}, + {:styler, "~> 1.4", only: [:dev, :test], runtime: false}, {:typed_struct, "~> 0.3.0"} ] end diff --git a/mix.lock b/mix.lock index 4431b70..f69e73b 100644 --- a/mix.lock +++ b/mix.lock @@ -7,6 +7,6 @@ "makeup_erlang": {:hex, :makeup_erlang, "1.0.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"}, "nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"}, "process_tree": {:hex, :process_tree, "0.2.0", "a934111ff00c1b696731edf35ecc6a4724105e045efa95a1977cac21ebad62cd", [:mix], [], "hexpm", "92901d3e9d2f40f4e09c7774a97ed0d1fac5d3541c62552df8346dfa0f9ee19b"}, - "styler": {:hex, :styler, "1.2.1", "28f9e3d4b065c22575c56b8ae03d05188add1b21bec5ae664fc1551e2dfcc41b", [:mix], [], "hexpm", "71dc33980e530d21ca54db9c2075e646faa6e7b744a9d4a3dfb0ff01f56595f0"}, + "styler": {:hex, :styler, "1.4.2", "420da8a9d10324625b75690ca9f2468bc00ee6eb78dead827e562368f9feabbb", [:mix], [], "hexpm", "ca22538b203b2424eef99a227e081143b9a9a4b26da75f26d920537fcd778832"}, "typed_struct": {:hex, :typed_struct, "0.3.0", "939789e3c1dca39d7170c87f729127469d1315dcf99fee8e152bb774b17e7ff7", [:mix], [], "hexpm", "c50bd5c3a61fe4e198a8504f939be3d3c85903b382bde4865579bc23111d1b6d"}, } From 2306dc2f534b22e0c37d624b313177e02628da84 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Sat, 7 Jun 2025 23:25:38 -0700 Subject: [PATCH 09/10] =?UTF-8?q?=F0=9F=91=B7=20Change=20failure=20setting?= =?UTF-8?q?s=20for=20CI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set fail-fast to false to allow all versions to run to completion in order to get full feedback on a change. Also, mark the `main` build as `experimental` because it may fail from time to time. --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7f8314..b562e2c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,6 +17,7 @@ jobs: name: Checks/Tests on OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}} runs-on: ubuntu-latest strategy: + fail-fast: false matrix: include: - elixir: '1.14' @@ -32,6 +33,7 @@ jobs: - elixir: '1.19' otp: '28' - elixir: main + experimental: true otp: '28' steps: - name: Set up Elixir From 46bcc6fc65d78d08016852924ffd198657c793e8 Mon Sep 17 00:00:00 2001 From: Randy Coulman Date: Mon, 9 Jun 2025 19:22:41 -0700 Subject: [PATCH 10/10] =?UTF-8?q?=F0=9F=91=BD=20Address=20forthcoming=20de?= =?UTF-8?q?precation=20in=20Elixir=201.20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ExUnit in Elixir 1.20 deprecates `ExUnit.Filters.parse_path/1` in favor of `ExUnit.Filters.parse_paths/1`. However, the newer function is only available in Elixir 1.16 and newer and we (and Elixir) still support 1.14 and 1.15. To address this, we conditionally use the newer function in 1.20 and above (including pre-releases) and the older function otherwise. --- lib/mix_test_interactive/pattern_filter.ex | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/mix_test_interactive/pattern_filter.ex b/lib/mix_test_interactive/pattern_filter.ex index 59085c7..0ca288c 100644 --- a/lib/mix_test_interactive/pattern_filter.ex +++ b/lib/mix_test_interactive/pattern_filter.ex @@ -25,10 +25,19 @@ defmodule MixTestInteractive.PatternFilter do |> Kernel.++(with_line_number) end - defp is_line_number_pattern?(pattern) do - case ExUnit.Filters.parse_path(pattern) do - {_path, []} -> false - _ -> true + if Version.compare(System.version(), "1.20.0-dev") == :lt do + defp is_line_number_pattern?(pattern) do + case ExUnit.Filters.parse_path(pattern) do + {_path, []} -> false + _ -> true + end + end + else + defp is_line_number_pattern?(pattern) do + case ExUnit.Filters.parse_paths([pattern]) do + {_path, []} -> false + _ -> true + end end end end