Skip to content

Commit

Permalink
feat!: make profile a task option
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the profile is no longer the first argument of `mix nodelix`
  • Loading branch information
sheerlox committed Nov 29, 2023
1 parent 5dfe8d9 commit 47d05ed
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 67 deletions.
56 changes: 41 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
35 changes: 18 additions & 17 deletions lib/mix/tasks/nodelix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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}
>
Expand All @@ -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)
Expand All @@ -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
Expand All @@ -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
8 changes: 4 additions & 4 deletions lib/mix/tasks/nodelix.install.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
71 changes: 46 additions & 25 deletions lib/nodelix.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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) ||
Expand Down Expand Up @@ -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!(),
Expand All @@ -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`.
"""
Expand Down
12 changes: 6 additions & 6 deletions test/nodelix_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -30,15 +30,15 @@ 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)

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

Expand Down

0 comments on commit 47d05ed

Please sign in to comment.