From 47d05ed813bf5893ffabf56c93a7e80bfa8a9383 Mon Sep 17 00:00:00 2001 From: Pierre Cavin Date: Wed, 29 Nov 2023 14:52:36 +0100 Subject: [PATCH] feat!: make profile a task option BREAKING CHANGE: the profile is no longer the first argument of `mix nodelix` --- README.md | 56 ++++++++++++++++++------- lib/mix/tasks/nodelix.ex | 35 ++++++++-------- lib/mix/tasks/nodelix.install.ex | 8 ++-- lib/nodelix.ex | 71 +++++++++++++++++++++----------- test/nodelix_test.exs | 12 +++--- 5 files changed, 115 insertions(+), 67 deletions(-) diff --git a/README.md b/README.md index 320af8e..c2912fd 100644 --- a/README.md +++ b/README.md @@ -45,31 +45,57 @@ $ mix nodelix.install And invoke Node.js with: ```shell -$ mix nodelix default some-script.js --some-option +$ mix nodelix some-script.js --some-option ``` The Node.js installation is located at `_build/dev/nodejs/versions/$VERSION`. +## Nodelix configuration + +There are two global configurations for the nodelix application: + +- `:version` - the Node.js version to use + +- `:cacerts_path` - the directory to find certificates for + https connections + ## Profiles -You can define multiple nodelix profiles. By default, there is a -profile called `:default` which you can configure its args, current -directory and environment: +You can define multiple nodelix profiles. There is a default empty profile +which you can configure its args, current directory and environment: - config :nodelix, - version: "20.10.0", - default: [ - args: ~w( - some-script.js - --some-option - ), - cd: Path.expand("../assets", __DIR__), - ] +```elixir +config :nodelix, + version: "20.10.0", + default: [ + args: ~w( + some-script.js + --some-option + ), + cd: Path.expand("../assets", __DIR__) + ], + custom: [ + args: ~w( + another-script.js + --another-option + ), + cd: Path.expand("../assets/scripts", __DIR__), + env: [ + NODE_DEBUG: "*" + ] + ] +``` The default current directory is your project's root. -When `mix nodelix default` is invoked, the task arguments will be appended -to the ones configured above. +To use a profile other than `default`, you can use the `--profile` option: + +```shell +mix nodelix --profile custom +``` + +When `mix nodelix` is invoked, the task arguments will +be appended to the ones configured in the profile. ## Versioning diff --git a/lib/mix/tasks/nodelix.ex b/lib/mix/tasks/nodelix.ex index 60a0649..c5e8312 100644 --- a/lib/mix/tasks/nodelix.ex +++ b/lib/mix/tasks/nodelix.ex @@ -2,7 +2,7 @@ defmodule Mix.Tasks.Nodelix do use Mix.Task @moduledoc """ - Invokes `node` with the given args. + Invokes `node` with the provided arguments. > ### Warning {: .warning} > @@ -11,35 +11,38 @@ defmodule Mix.Tasks.Nodelix do Usage: - $ mix nodelix TASK_OPTIONS PROFILE NODE_ARGS + $ mix nodelix TASK_OPTIONS NODE_ARGS Example: - $ mix nodelix default some-script.js --some-option + $ mix nodelix some-script.js --some-option - If Node.js is not installed, it is automatically downloaded. - The arguments given to this task will be appended - to any configured arguments. + Refer to `Nodelix` for more information on configuration and profiles. ## Options - * `--runtime-config` - load the runtime configuration + - `--profile` - name of the profile to use + + - `--runtime-config` - load the runtime configuration before executing command Flags to control this Mix task must be given before the - profile: + node arguments: + + $ mix nodelix --profile default --runtime-config some-script.js --some-option - $ mix nodelix --runtime-config default some-script.js --some-option """ - @shortdoc "Invokes node with the profile and args" + @shortdoc "Invokes node with the provided arguments" @compile {:no_warn_undefined, Mix} @dialyzer {:no_missing_calls, run: 1} @impl Mix.Task def run(args) do - switches = [runtime_config: :boolean] - {opts, remaining_args} = OptionParser.parse_head!(args, switches: switches) + switches = [profile: :string, runtime_config: :boolean] + + {opts, remaining_args, invalid_opts} = OptionParser.parse_head(args, strict: switches) + node_args = Enum.map(invalid_opts, &elem(&1, 0)) ++ remaining_args if function_exported?(Mix, :ensure_application!, 1) do Mix.ensure_application!(:inets) @@ -53,8 +56,10 @@ defmodule Mix.Tasks.Nodelix do Application.ensure_all_started(:nodelix) end + profile = opts[:profile] || "default" + Mix.Task.reenable("nodelix") - install_and_run(remaining_args) + install_and_run([profile | node_args]) end defp install_and_run([profile | args] = all) do @@ -63,8 +68,4 @@ defmodule Mix.Tasks.Nodelix do status -> Mix.raise("`mix nodelix #{Enum.join(all, " ")}` exited with #{status}") end end - - defp install_and_run([]) do - Mix.raise("`mix nodelix` expects the profile as argument") - end end diff --git a/lib/mix/tasks/nodelix.install.ex b/lib/mix/tasks/nodelix.install.ex index 0c9cb0e..8e8ce3e 100644 --- a/lib/mix/tasks/nodelix.install.ex +++ b/lib/mix/tasks/nodelix.install.ex @@ -21,11 +21,11 @@ defmodule Mix.Tasks.Nodelix.Install do ## Options - * `--runtime-config` - load the runtime configuration - before executing command + - `--runtime-config` - load the runtime configuration + before executing command - * `--if-missing` - install only if the given version - does not exist + - `--if-missing` - install only if the given version + does not exist """ @shortdoc "Installs Node.js" diff --git a/lib/nodelix.ex b/lib/nodelix.ex index c8a21d7..6836a4b 100644 --- a/lib/nodelix.ex +++ b/lib/nodelix.ex @@ -12,42 +12,64 @@ defmodule Nodelix do > This is a pre-release version. As such, anything _may_ change at any time, the public > API _should not_ be considered stable, and using a pinned version is _recommended_. - ## Profiles + ## Nodelix configuration - You can define multiple nodelix profiles. By default, there is a - profile called `:default` which you can configure its args, current - directory and environment: + There are two global configurations for the nodelix application: - config :nodelix, - version: "#{VersionManager.latest_lts_version()}", - default: [ - args: ~w( - some-script.js - --some-option - ), - cd: Path.expand("../assets", __DIR__), - ] + - `:version` - the Node.js version to use - The default current directory is your project's root. + - `:cacerts_path` - the directory to find certificates for + https connections - ## Nodelix configuration + ## Profiles - There are two global configurations for the nodelix application: + You can define multiple nodelix profiles. There is a default empty profile + which you can configure its args, current directory and environment: + + ```elixir + config :nodelix, + version: "#{VersionManager.latest_lts_version()}", + default: [ + args: ~w( + some-script.js + --some-option + ), + cd: Path.expand("../assets", __DIR__), + ], + custom: [ + args: ~w( + another-script.js + --another-option + ), + cd: Path.expand("../assets/scripts", __DIR__), + ] + ``` + + The default current directory is your project's root. - * `:version` - the expected Node.js version + To use a profile other than `default`, you can use + the `--profile` option: - * `:cacerts_path` - the directory to find certificates for - https connections + ```shell + mix nodelix --profile custom + ``` + + When `mix nodelix` is invoked, the task arguments will + be appended to the ones configured in the profile. """ @doc false def start(_, _) do unless Application.get_env(:nodelix, :version) do + latest_lts_version = VersionManager.latest_lts_version() + Logger.warning(""" Node.js version is not configured. Please set it in your config files: - config :nodelix, :version, "#{VersionManager.latest_lts_version()}" + config :nodelix, :version, "#{latest_lts_version}" + + Using latest known LTS version at the time of release: #{latest_lts_version} """) end @@ -64,7 +86,7 @@ defmodule Nodelix 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(:nodelix, profile) || @@ -96,9 +118,7 @@ defmodule Nodelix do if length(args) == 0, do: raise(ArgumentError, "No argument provided.") - env = - config - |> Keyword.get(:env, %{}) + env = Keyword.get(config, :env, %{}) opts = [ cd: config[:cd] || File.cwd!(), @@ -113,7 +133,8 @@ defmodule Nodelix do end @doc """ - Installs, if not available, and then runs `node`. + Installs Node.js if the configured version is not available, + and then runs `node`. Returns the same as `run/2`. """ diff --git a/test/nodelix_test.exs b/test/nodelix_test.exs index bcfd9bb..d5f7d76 100644 --- a/test/nodelix_test.exs +++ b/test/nodelix_test.exs @@ -13,15 +13,15 @@ defmodule NodelixTest do :ok end - test "run on default" do + test "run without profile" do assert ExUnit.CaptureIO.capture_io(fn -> - assert Mix.Task.run("nodelix", ["default", "--version"]) == :ok + assert Mix.Task.run("nodelix", ["--version"]) == :ok end) =~ @version end - test "run on profile" do + test "run on another profile" do assert ExUnit.CaptureIO.capture_io(fn -> - assert Mix.Task.run("nodelix", ["another", "--version"]) == :ok + assert Mix.Task.run("nodelix", ["--profile", "another", "--version"]) == :ok end) =~ @version end @@ -30,7 +30,7 @@ defmodule NodelixTest do Mix.Task.rerun("nodelix.install", ["--if-missing"]) assert ExUnit.CaptureIO.capture_io(fn -> - assert Mix.Task.run("nodelix", ["default", "--version"]) == :ok + assert Mix.Task.run("nodelix", ["--version"]) == :ok end) =~ "20.9.0" Application.delete_env(:nodelix, :version) @@ -38,7 +38,7 @@ defmodule NodelixTest do Mix.Task.rerun("nodelix.install", ["--if-missing"]) assert ExUnit.CaptureIO.capture_io(fn -> - assert Mix.Task.run("nodelix", ["default", "--version"]) == :ok + assert Mix.Task.run("nodelix", ["--version"]) == :ok end) =~ @version end