From bcd67073bbdc8234871aa90e5977b3b24130338c Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 11:53:54 +0200 Subject: [PATCH 01/12] Support configuring version per profile --- lib/mix/tasks/tailwind.install.ex | 46 +++++++++------- lib/tailwind.ex | 87 +++++++++++++++++++------------ 2 files changed, 80 insertions(+), 53 deletions(-) diff --git a/lib/mix/tasks/tailwind.install.ex b/lib/mix/tasks/tailwind.install.ex index 0e8fe0a..5495084 100644 --- a/lib/mix/tasks/tailwind.install.ex +++ b/lib/mix/tasks/tailwind.install.ex @@ -2,6 +2,12 @@ defmodule Mix.Tasks.Tailwind.Install do @moduledoc """ Installs Tailwind executable and assets. + Usage: + + $ mix tailwind.install TASK_OPTIONS BASE_URL + + Example: + $ mix tailwind.install $ mix tailwind.install --if-missing @@ -15,9 +21,7 @@ defmodule Mix.Tasks.Tailwind.Install do binary (beware that we cannot guarantee the compatibility of any third party executable): - ```bash - $ mix tailwind.install https://people.freebsd.org/~dch/pub/tailwind/v3.2.6/tailwindcss-freebsd-x64 - ``` + $ mix tailwind.install https://people.freebsd.org/~dch/pub/tailwind/$version/tailwindcss-$target ## Options @@ -79,29 +83,31 @@ defmodule Mix.Tasks.Tailwind.Install do if opts[:runtime_config], do: Mix.Task.run("app.config") - if opts[:if_missing] && latest_version?() do - :ok - else - if Keyword.get(opts, :assets, true) do - File.mkdir_p!("assets/css") + for {profile, _} <- Tailwind.profiles() do + if opts[:if_missing] && latest_version?(profile) do + :ok + else + if Keyword.get(opts, :assets, true) do + File.mkdir_p!("assets/css") - prepare_app_css() - prepare_app_js() - end + prepare_app_css() + prepare_app_js() + end - if function_exported?(Mix, :ensure_application!, 1) do - Mix.ensure_application!(:inets) - Mix.ensure_application!(:ssl) - end + if function_exported?(Mix, :ensure_application!, 1) do + Mix.ensure_application!(:inets) + Mix.ensure_application!(:ssl) + end - Mix.Task.run("loadpaths") - Tailwind.install(base_url) + Mix.Task.run("loadpaths") + Tailwind.install(profile, base_url) + end end end - defp latest_version?() do - version = Tailwind.configured_version() - match?({:ok, ^version}, Tailwind.bin_version()) + defp latest_version?(profile) do + version = Tailwind.configured_version!(profile) + match?({:ok, ^version}, Tailwind.bin_version(profile)) end defp prepare_app_css do diff --git a/lib/tailwind.ex b/lib/tailwind.ex index 63b3115..2d0a19b 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -81,20 +81,22 @@ defmodule Tailwind do """) end - configured_version = configured_version() - - case bin_version() do - {:ok, ^configured_version} -> - :ok - - {:ok, version} -> - Logger.warning(""" - Outdated tailwind version. Expected #{configured_version}, got #{version}. \ - Please run `mix tailwind.install` or update the version in your config files.\ - """) - - :error -> - :ok + for {profile, config} <- profiles() do + configured_version = Keyword.get(config, :version, global_version()) + + case bin_version(profile) do + {:ok, ^configured_version} -> + :ok + + {:ok, version} -> + Logger.warning(""" + Outdated tailwind version. Expected #{configured_version}, got #{version}. \ + Please run `mix tailwind.install` or update the version in your config files.\ + """) + + :error -> + :ok + end end end @@ -105,18 +107,36 @@ defmodule Tailwind do # Latest known version at the time of publishing. def latest_version, do: @latest_version + @doc false + def profiles do + config_keys = [:version_check, :version, :target, :path] + :tailwind |> Application.get_all_env() |> Keyword.drop(config_keys) + end + @doc """ Returns the configured tailwind version. """ - def configured_version do + def global_version do Application.get_env(:tailwind, :version, latest_version()) end + @doc """ + Returns the configured tailwind version for a specific profile. + + If not explicitly configured, falls back to `global_version/0`. + Raises if the given profile does not exist. + """ + def configured_version!(profile) when is_atom(profile) do + :tailwind + |> Application.fetch_env!(profile) + |> Keyword.get(:version, global_version()) + end + @doc """ Returns the configured tailwind target. By default, it is automatically detected. """ def configured_target do - Application.get_env(:tailwind, :target, target()) + Application.get_env(:tailwind, :target, system_target()) end @doc """ @@ -146,8 +166,8 @@ defmodule Tailwind do The executable may not be available if it was not yet installed. """ - def bin_path do - name = "tailwind-#{configured_target()}" + def bin_path(profile \\ :default) do + name = "tailwind-#{configured_target()}-#{configured_version!(profile)}" Application.get_env(:tailwind, :path) || if Code.ensure_loaded?(Mix.Project) do @@ -163,8 +183,8 @@ defmodule Tailwind do Returns `{:ok, version_string}` on success or `:error` when the executable is not available. """ - def bin_version do - path = bin_path() + def bin_version(profile \\ :default) do + path = bin_path(profile) with true <- File.exists?(path), {out, 0} <- System.cmd(path, ["--help"]), @@ -176,7 +196,7 @@ defmodule Tailwind do end @doc """ - Runs the given command with `args`. + Runs the tailwind CLI for the given `profile` with `args`. The given args will be appended to the configured args. The task output will be streamed directly to stdio. It @@ -198,7 +218,8 @@ defmodule Tailwind do stderr_to_stdout: true ] - bin_path() + profile + |> bin_path() |> System.cmd(args ++ extra_args, opts) |> elem(1) end @@ -213,8 +234,8 @@ defmodule Tailwind do Returns the same as `run/2`. """ def install_and_run(profile, args) do - unless File.exists?(bin_path()) do - install() + unless File.exists?(bin_path(profile)) do + install(profile) end run(profile, args) @@ -228,11 +249,11 @@ defmodule Tailwind do end @doc """ - Installs tailwind with `configured_version/0`. + Installs tailwind with `configured_version!/1`. """ - def install(base_url \\ default_base_url()) do - url = get_url(base_url) - bin_path = bin_path() + def install(profile \\ :default, base_url \\ default_base_url()) do + url = get_url(profile, base_url) + bin_path = bin_path(profile) binary = fetch_body!(url) File.mkdir_p!(Path.dirname(bin_path)) @@ -255,7 +276,7 @@ defmodule Tailwind do # tailwindcss-macos-arm64 # tailwindcss-macos-x64 # tailwindcss-windows-x64.exe - defp target do + defp system_target do arch_str = :erlang.system_info(:system_architecture) target_triple = arch_str |> List.to_string() |> String.split("-") @@ -303,7 +324,7 @@ defmodule Tailwind do # Tailwind CLI v4+ added explicit musl versions for Linux as # tailwind-linux-x64-musl # tailwind-linux-arm64-musl - if Version.match?(configured_version(), "~> 4.0") do + if Version.match?(global_version(), "~> 4.0") do "-musl" else "" @@ -356,7 +377,7 @@ defmodule Tailwind do You can see the available files for the configured version at: - https://github.com/tailwindlabs/tailwindcss/releases/tag/v#{configured_version()} + https://github.com/tailwindlabs/tailwindcss/releases/tag/v#{global_version()} """ {true, {:error, {:failed_connect, [{:to_address, _}, {inet, _, reason}]}}} @@ -420,9 +441,9 @@ defmodule Tailwind do :erlang.system_info(:otp_release) |> List.to_integer() end - defp get_url(base_url) do + defp get_url(profile, base_url) do base_url - |> String.replace("$version", configured_version()) + |> String.replace("$version", configured_version!(profile)) |> String.replace("$target", configured_target()) end end From 9f9a2373a3b28b46233a9ba21b0e6219a1b89b0c Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 13:17:27 +0200 Subject: [PATCH 02/12] Rename back to `configured_version` --- lib/tailwind.ex | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/tailwind.ex b/lib/tailwind.ex index 2d0a19b..310efbc 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -82,7 +82,7 @@ defmodule Tailwind do end for {profile, config} <- profiles() do - configured_version = Keyword.get(config, :version, global_version()) + configured_version = Keyword.get(config, :version, configured_version()) case bin_version(profile) do {:ok, ^configured_version} -> @@ -116,20 +116,20 @@ defmodule Tailwind do @doc """ Returns the configured tailwind version. """ - def global_version do + def configured_version do Application.get_env(:tailwind, :version, latest_version()) end @doc """ Returns the configured tailwind version for a specific profile. - If not explicitly configured, falls back to `global_version/0`. + If not explicitly configured, falls back to `configured_version/0`. Raises if the given profile does not exist. """ def configured_version!(profile) when is_atom(profile) do :tailwind |> Application.fetch_env!(profile) - |> Keyword.get(:version, global_version()) + |> Keyword.get(:version, configured_version()) end @doc """ @@ -324,7 +324,7 @@ defmodule Tailwind do # Tailwind CLI v4+ added explicit musl versions for Linux as # tailwind-linux-x64-musl # tailwind-linux-arm64-musl - if Version.match?(global_version(), "~> 4.0") do + if Version.match?(configured_version(), "~> 4.0") do "-musl" else "" @@ -377,7 +377,7 @@ defmodule Tailwind do You can see the available files for the configured version at: - https://github.com/tailwindlabs/tailwindcss/releases/tag/v#{global_version()} + https://github.com/tailwindlabs/tailwindcss/releases/tag/v#{configured_version()} """ {true, {:error, {:failed_connect, [{:to_address, _}, {inet, _, reason}]}}} From 2669b810334ada54f497dd8e55c726dabb513488 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 13:19:18 +0200 Subject: [PATCH 03/12] Don't change default argument order --- lib/mix/tasks/tailwind.install.ex | 2 +- lib/tailwind.ex | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/mix/tasks/tailwind.install.ex b/lib/mix/tasks/tailwind.install.ex index 5495084..9bfec48 100644 --- a/lib/mix/tasks/tailwind.install.ex +++ b/lib/mix/tasks/tailwind.install.ex @@ -100,7 +100,7 @@ defmodule Mix.Tasks.Tailwind.Install do end Mix.Task.run("loadpaths") - Tailwind.install(profile, base_url) + Tailwind.install(base_url, profile) end end end diff --git a/lib/tailwind.ex b/lib/tailwind.ex index 310efbc..b97ebbe 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -235,7 +235,7 @@ defmodule Tailwind do """ def install_and_run(profile, args) do unless File.exists?(bin_path(profile)) do - install(profile) + install(default_base_url(), profile) end run(profile, args) @@ -251,7 +251,7 @@ defmodule Tailwind do @doc """ Installs tailwind with `configured_version!/1`. """ - def install(profile \\ :default, base_url \\ default_base_url()) do + def install(base_url \\ default_base_url(), profile \\ :default) do url = get_url(profile, base_url) bin_path = bin_path(profile) binary = fetch_body!(url) From 57b26bcae23cdcbffcc5179145124cea149590ae Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 13:21:53 +0200 Subject: [PATCH 04/12] `configured_version` -> `configured_version!` --- lib/mix/tasks/tailwind.install.ex | 2 +- lib/tailwind.ex | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/mix/tasks/tailwind.install.ex b/lib/mix/tasks/tailwind.install.ex index 9bfec48..2405eb7 100644 --- a/lib/mix/tasks/tailwind.install.ex +++ b/lib/mix/tasks/tailwind.install.ex @@ -106,7 +106,7 @@ defmodule Mix.Tasks.Tailwind.Install do end defp latest_version?(profile) do - version = Tailwind.configured_version!(profile) + version = Tailwind.configured_version(profile) match?({:ok, ^version}, Tailwind.bin_version(profile)) end diff --git a/lib/tailwind.ex b/lib/tailwind.ex index b97ebbe..6a5aef6 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -126,9 +126,9 @@ defmodule Tailwind do If not explicitly configured, falls back to `configured_version/0`. Raises if the given profile does not exist. """ - def configured_version!(profile) when is_atom(profile) do + def configured_version(profile) when is_atom(profile) do :tailwind - |> Application.fetch_env!(profile) + |> Application.get_env(profile, []) |> Keyword.get(:version, configured_version()) end @@ -167,7 +167,7 @@ defmodule Tailwind do The executable may not be available if it was not yet installed. """ def bin_path(profile \\ :default) do - name = "tailwind-#{configured_target()}-#{configured_version!(profile)}" + name = "tailwind-#{configured_target()}-#{configured_version(profile)}" Application.get_env(:tailwind, :path) || if Code.ensure_loaded?(Mix.Project) do @@ -249,7 +249,7 @@ defmodule Tailwind do end @doc """ - Installs tailwind with `configured_version!/1`. + Installs tailwind with `configured_version/1`. """ def install(base_url \\ default_base_url(), profile \\ :default) do url = get_url(profile, base_url) @@ -443,7 +443,7 @@ defmodule Tailwind do defp get_url(profile, base_url) do base_url - |> String.replace("$version", configured_version!(profile)) + |> String.replace("$version", configured_version(profile)) |> String.replace("$target", configured_target()) end end From b3009643176bdb4dd4f3fa4c753ccdfb2ef65534 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 13:40:38 +0200 Subject: [PATCH 05/12] Collect & filter required versions before installing --- lib/mix/tasks/tailwind.install.ex | 12 ++++++--- lib/tailwind.ex | 44 ++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/mix/tasks/tailwind.install.ex b/lib/mix/tasks/tailwind.install.ex index 2405eb7..4c7f9b4 100644 --- a/lib/mix/tasks/tailwind.install.ex +++ b/lib/mix/tasks/tailwind.install.ex @@ -83,8 +83,8 @@ defmodule Mix.Tasks.Tailwind.Install do if opts[:runtime_config], do: Mix.Task.run("app.config") - for {profile, _} <- Tailwind.profiles() do - if opts[:if_missing] && latest_version?(profile) do + for {version, latest?} <- collect_versions() do + if opts[:if_missing] && latest? do :ok else if Keyword.get(opts, :assets, true) do @@ -100,11 +100,17 @@ defmodule Mix.Tasks.Tailwind.Install do end Mix.Task.run("loadpaths") - Tailwind.install(base_url, profile) + Tailwind.install(base_url, version) end end end + defp collect_versions do + for {profile, _} <- Tailwind.profiles(), uniq: true do + {Tailwind.configured_version(profile), latest_version?(profile)} + end + end + defp latest_version?(profile) do version = Tailwind.configured_version(profile) match?({:ok, ^version}, Tailwind.bin_version(profile)) diff --git a/lib/tailwind.ex b/lib/tailwind.ex index 6a5aef6..c62f9f5 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -166,8 +166,10 @@ defmodule Tailwind do The executable may not be available if it was not yet installed. """ - def bin_path(profile \\ :default) do - name = "tailwind-#{configured_target()}-#{configured_version(profile)}" + def bin_path, do: bin_path(configured_version()) + + def bin_path(version) do + name = "tailwind-#{configured_target()}-#{version}" Application.get_env(:tailwind, :path) || if Code.ensure_loaded?(Mix.Project) do @@ -183,9 +185,20 @@ defmodule Tailwind do Returns `{:ok, version_string}` on success or `:error` when the executable is not available. """ - def bin_version(profile \\ :default) do - path = bin_path(profile) + def bin_version do + configured_version() + |> bin_path() + |> get_version() + end + + def bin_version(profile) when is_atom(profile) do + profile + |> configured_version() + |> bin_path() + |> get_version() + end + defp get_version(path) do with true <- File.exists?(path), {out, 0} <- System.cmd(path, ["--help"]), [vsn] <- Regex.run(~r/tailwindcss v([^\s]+)/, out, capture: :all_but_first) do @@ -219,6 +232,7 @@ defmodule Tailwind do ] profile + |> configured_version() |> bin_path() |> System.cmd(args ++ extra_args, opts) |> elem(1) @@ -233,9 +247,11 @@ defmodule Tailwind do Returns the same as `run/2`. """ - def install_and_run(profile, args) do - unless File.exists?(bin_path(profile)) do - install(default_base_url(), profile) + def install_and_run(profile, args) when is_atom(profile) do + version = configured_version(profile) + + unless File.exists?(bin_path(version)) do + install(default_base_url(), version) end run(profile, args) @@ -251,9 +267,13 @@ defmodule Tailwind do @doc """ Installs tailwind with `configured_version/1`. """ - def install(base_url \\ default_base_url(), profile \\ :default) do - url = get_url(profile, base_url) - bin_path = bin_path(profile) + def install(base_url \\ default_base_url()) do + install(base_url, configured_version()) + end + + def install(base_url, version) do + url = get_url(base_url, version) + bin_path = bin_path(version) binary = fetch_body!(url) File.mkdir_p!(Path.dirname(bin_path)) @@ -441,9 +461,9 @@ defmodule Tailwind do :erlang.system_info(:otp_release) |> List.to_integer() end - defp get_url(profile, base_url) do + defp get_url(base_url, version) do base_url - |> String.replace("$version", configured_version(profile)) + |> String.replace("$version", version) |> String.replace("$target", configured_target()) end end From 087dd35683b5ef9b77f2abab13fdf5f1f4a781d3 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 14:17:20 +0200 Subject: [PATCH 06/12] Update lib/tailwind.ex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Valim --- lib/tailwind.ex | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/tailwind.ex b/lib/tailwind.ex index c62f9f5..ab459b3 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -166,9 +166,7 @@ defmodule Tailwind do The executable may not be available if it was not yet installed. """ - def bin_path, do: bin_path(configured_version()) - - def bin_path(version) do + def bin_path(version \\ configured_version()) do name = "tailwind-#{configured_target()}-#{version}" Application.get_env(:tailwind, :path) || From c2c42c442c76061b0b974c5f85f246796d78daf5 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 14:17:39 +0200 Subject: [PATCH 07/12] Update lib/tailwind.ex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: José Valim --- lib/tailwind.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tailwind.ex b/lib/tailwind.ex index ab459b3..1b2c091 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -263,7 +263,7 @@ defmodule Tailwind do end @doc """ - Installs tailwind with `configured_version/1`. + Installs tailwind with `configured_version/0`. """ def install(base_url \\ default_base_url()) do install(base_url, configured_version()) From dc5d41f73f0c2d45a0aa075295c62c88619513a6 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 14:26:07 +0200 Subject: [PATCH 08/12] Filter profiles for keyword lists --- lib/tailwind.ex | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/tailwind.ex b/lib/tailwind.ex index 1b2c091..5e72403 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -110,7 +110,11 @@ defmodule Tailwind do @doc false def profiles do config_keys = [:version_check, :version, :target, :path] - :tailwind |> Application.get_all_env() |> Keyword.drop(config_keys) + + :tailwind + |> Application.get_all_env() + |> Keyword.drop(config_keys) + |> Enum.filter(&Keyword.keyword?(elem(&1, 1))) end @doc """ From f8bf6b9902c39106e5bddc86d182ca35234b478d Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 14:27:28 +0200 Subject: [PATCH 09/12] Mark `@doc false` on `install/2` --- lib/tailwind.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tailwind.ex b/lib/tailwind.ex index 5e72403..eace793 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -273,6 +273,7 @@ defmodule Tailwind do install(base_url, configured_version()) end + @doc false def install(base_url, version) do url = get_url(base_url, version) bin_path = bin_path(version) From eac9354e2ec3691658ae95fcf0d3166f98f7050a Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 14:37:49 +0200 Subject: [PATCH 10/12] Improve documentation --- lib/tailwind.ex | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/tailwind.ex b/lib/tailwind.ex index eace793..7086170 100644 --- a/lib/tailwind.ex +++ b/lib/tailwind.ex @@ -21,6 +21,9 @@ defmodule Tailwind do cd: Path.expand("../assets", __DIR__), ] + It is also possible to override the required tailwind CLI version on + profile-basis. + ## Tailwind configuration There are four global configurations for the tailwind application: @@ -146,7 +149,7 @@ defmodule Tailwind do @doc """ Returns the configuration for the given profile. - Returns nil if the profile does not exist. + Raises if the profile does not exist. """ def config_for!(profile) when is_atom(profile) do Application.get_env(:tailwind, profile) || @@ -166,7 +169,7 @@ defmodule Tailwind do end @doc """ - Returns the path to the executable. + Returns the path to the executable for the given `version`. The executable may not be available if it was not yet installed. """ @@ -182,9 +185,9 @@ defmodule Tailwind do end @doc """ - Returns the version of the tailwind executable. + Returns the version of the executable. - Returns `{:ok, version_string}` on success or `:error` when the executable + Returns `{:ok, vsn}` on success or `:error` when the executable is not available. """ def bin_version do @@ -245,7 +248,7 @@ defmodule Tailwind do end @doc """ - Installs, if not available, and then runs `tailwind`. + Installs, if not available, and then runs the tailwind CLI. Returns the same as `run/2`. """ @@ -260,7 +263,7 @@ defmodule Tailwind do end @doc """ - The default URL to install Tailwind from. + Returns the default URL to install Tailwind from. """ def default_base_url do "https://github.com/tailwindlabs/tailwindcss/releases/download/v$version/tailwindcss-$target" @@ -268,6 +271,9 @@ defmodule Tailwind do @doc """ Installs tailwind with `configured_version/0`. + + If given, the executable is downloaded from `base_url`, + otherwise, `default_base_url/0` is used. """ def install(base_url \\ default_base_url()) do install(base_url, configured_version()) From f002055eb4f0ea82dd55be94f4baaf892ab12c07 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 16:20:56 +0200 Subject: [PATCH 11/12] Fix broken test --- lib/mix/tasks/tailwind.install.ex | 55 ++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/lib/mix/tasks/tailwind.install.ex b/lib/mix/tasks/tailwind.install.ex index 4c7f9b4..6489802 100644 --- a/lib/mix/tasks/tailwind.install.ex +++ b/lib/mix/tasks/tailwind.install.ex @@ -60,26 +60,15 @@ defmodule Mix.Tasks.Tailwind.Install do @impl true def run(args) do - valid_options = [runtime_config: :boolean, if_missing: :boolean, assets: :boolean] - - {opts, base_url} = - case OptionParser.parse_head!(args, strict: valid_options) do - {opts, []} -> - {opts, Tailwind.default_base_url()} - - {opts, [base_url]} -> - {opts, base_url} - - {_, _} -> - Mix.raise(""" - Invalid arguments to tailwind.install, expected one of: + if args |> try_install() |> was_successful?() do + :ok + else + :error + end + end - mix tailwind.install - mix tailwind.install 'https://github.com/tailwindlabs/tailwindcss/releases/download/v$version/tailwindcss-$target' - mix tailwind.install --runtime-config - mix tailwind.install --if-missing - """) - end + defp try_install(args) do + {opts, base_url} = parse_arguments(args) if opts[:runtime_config], do: Mix.Task.run("app.config") @@ -105,12 +94,36 @@ defmodule Mix.Tasks.Tailwind.Install do end end + defp parse_arguments(args) do + case OptionParser.parse_head!(args, strict: schema()) do + {opts, []} -> + {opts, Tailwind.default_base_url()} + + {opts, [base_url]} -> + {opts, base_url} + + {_, _} -> + Mix.raise(""" + Invalid arguments to tailwind.install, expected one of: + + mix tailwind.install + mix tailwind.install 'https://github.com/tailwindlabs/tailwindcss/releases/download/v$version/tailwindcss-$target' + mix tailwind.install --runtime-config + mix tailwind.install --if-missing + """) + end + end + defp collect_versions do for {profile, _} <- Tailwind.profiles(), uniq: true do {Tailwind.configured_version(profile), latest_version?(profile)} end end + defp was_successful?(results) do + Enum.all?(results, &(&1 == :ok)) + end + defp latest_version?(profile) do version = Tailwind.configured_version(profile) match?({:ok, ^version}, Tailwind.bin_version(profile)) @@ -145,4 +158,8 @@ defmodule Mix.Tasks.Tailwind.Install do :ok end end + + defp schema do + [runtime_config: :boolean, if_missing: :boolean, assets: :boolean] + end end From 790a89362fef51490f6c2aca94f14c9d6b104742 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 25 Apr 2025 16:31:27 +0200 Subject: [PATCH 12/12] Fix flaky test The `--if-missing` argument causes this test to be flaky. Previously, when you had downloaded another version, the newer version would automatically be missing and thus always be installed. Currently, that's no longer the case, because you can have multiple simultanous tailwind installs, so in this case (after the first test run), the newer version already exists. --- test/tailwind_test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tailwind_test.exs b/test/tailwind_test.exs index f583bac..a7aeb24 100644 --- a/test/tailwind_test.exs +++ b/test/tailwind_test.exs @@ -39,7 +39,7 @@ defmodule TailwindTest do Application.delete_env(:tailwind, :version) - Mix.Task.rerun("tailwind.install", ["--if-missing"]) + Mix.Task.rerun("tailwind.install", []) assert File.read!("assets/css/app.css") =~ "tailwind" assert ExUnit.CaptureIO.capture_io(fn ->