Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions lib/mix/tasks/nerves_time.install.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# SPDX-FileCopyrightText: 2025 Peter Ullrich
#
# SPDX-License-Identifier: Apache-2.0
#
defmodule Mix.Tasks.NervesTime.Install.Docs do
@moduledoc false

def short_doc do
"Installs NervesTime in your project using Igniter."
end

def example do
"mix igniter.install nerves_time"
end

def long_doc do
"""
#{short_doc()}

## Example

```bash
#{example()}
```

## Options

* `--file` or `-f` - Optional path to your `vm.args` file. Defaults to: `rel/vm.args.eex`
"""
end
end

if Code.ensure_loaded?(Igniter) do
defmodule Mix.Tasks.NervesTime.Install do
@shortdoc "#{__MODULE__.Docs.short_doc()}"

@moduledoc __MODULE__.Docs.long_doc()

use Igniter.Mix.Task

@impl Igniter.Mix.Task
def info(_argv, _composing_task) do
%Igniter.Mix.Task.Info{
# Groups allow for overlapping arguments for tasks by the same author
# See the generators guide for more.
group: :nerves_time,
# *other* dependencies to add
# i.e `{:foo, "~> 2.0"}`
adds_deps: [],
# *other* dependencies to add and call their associated installers, if they exist
# i.e `{:foo, "~> 2.0"}`
installs: [],
# An example invocation
example: __MODULE__.Docs.example(),
# A list of environments that this should be installed in.
only: nil,
# a list of positional arguments, i.e `[:file]`
positional: [],
# Other tasks your task composes using `Igniter.compose_task`, passing in the CLI argv
# This ensures your option schema includes options from nested tasks
composes: [],
# `OptionParser` schema
schema: [
file: :string
],
# Default values for the options in the `schema`
defaults: [
file: "rel/vm.args.eex"
],
# CLI aliases
aliases: [
f: :file
],
# A list of options in the schema that are required
required: []
}
end

@multi_time_warp_flag """

# Allow time warps so that the Erlang system time can more closely match the
# OS system time.
+C multi_time_warp
"""

@impl Igniter.Mix.Task
def igniter(igniter) do
args_path = igniter.args.options[:file]

igniter
|> Igniter.update_file(
args_path,
fn updater ->
Rewrite.Source.update(updater, :content, fn content ->
if content =~ "+C multi_time_warp" do
content
else
[header | rest] = String.split(content, "\n")
content = [header, @multi_time_warp_flag] ++ rest
Enum.join(content, "\n")
end
end)
end
)
end
end
else
defmodule Mix.Tasks.NervesTime.Install do
@shortdoc "#{__MODULE__.Docs.short_doc()} | Install `igniter` to use"

@moduledoc __MODULE__.Docs.long_doc()

use Mix.Task

def run(_argv) do
Mix.shell().error("""
The task 'nerves_time.install' requires igniter. Please install igniter and try again.

For more information, see: https://hexdocs.pm/igniter/readme.html#installation
""")

exit({:shutdown, 1})
end
end
end
10 changes: 10 additions & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ defmodule NervesTime.MixProject do
make_clean: ["clean"],
make_error_message: "",
docs: docs(),
aliases: aliases(),
start_permanent: Mix.env() == :prod,
consolidate_protocols: Mix.env() != :dev,
build_embedded: true,
dialyzer: [
flags: [:unmatched_returns, :error_handling, :missing_return, :extra_return, :underspecs]
Expand Down Expand Up @@ -63,6 +65,8 @@ defmodule NervesTime.MixProject do

defp deps do
[
# TODO: Set to `runtime: false` and fix Dialyzer issue.
{:igniter, "~> 0.5", optional: true, runtime: true},
{:muontrap, "~> 1.0 or ~> 0.5"},
{:credo, "~> 1.6", only: :dev, runtime: false},
{:elixir_make, "~> 0.6", runtime: false},
Expand All @@ -80,4 +84,10 @@ defmodule NervesTime.MixProject do
skip_undefined_reference_warnings_on: ["CHANGELOG.md"]
]
end

defp aliases() do
[
test: "test --warnings-as-errors"
]
end
end
16 changes: 16 additions & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,26 @@
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"ex_doc": {:hex, :ex_doc, "0.37.3", "f7816881a443cd77872b7d6118e8a55f547f49903aef8747dbcb345a75b462f9", [:mix], [{:earmark_parser, "~> 1.4.42", [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", "e6aebca7156e7c29b5da4daa17f6361205b2ae5f26e5c7d8ca0d3f7e18972233"},
"file_system": {:hex, :file_system, "1.1.0", "08d232062284546c6c34426997dd7ef6ec9f8bbd090eb91780283c9016840e8f", [:mix], [], "hexpm", "bfcf81244f416871f2a2e15c1b515287faa5db9c6bcf290222206d120b3d43f6"},
"finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"},
"glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
"hpax": {:hex, :hpax, "1.0.3", "ed67ef51ad4df91e75cc6a1494f851850c0bd98ebc0be6e81b026e765ee535aa", [:mix], [], "hexpm", "8eab6e1cfa8d5918c2ce4ba43588e894af35dbd8e91e6e55c817bca5847df34a"},
"igniter": {:hex, :igniter, "0.5.50", "2f6f3a50e02835e961b6228bfcdebe96cd6e9371042939e7f080c83049057e57", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "2e992df458c044f3a18ff6347275743b21092d6677368fdb8dfded321b85cc7b"},
"inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"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.2", "03e1804074b3aa64d5fad7aa64601ed0fb395337b982d9bcf04029d68d51b6a7", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "af33ff7ef368d5893e4a267933e7744e46ce3cf1f61e2dccf53a111ed3aa3727"},
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
"mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
"muontrap": {:hex, :muontrap, "1.6.0", "4e89bdd2cccaa8575f7160c06395ab26e1308e4a669b0f11992e1c8b6c601b03", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "8d8b3e7e337c3392cab902eb827fcd264a944b8bae2098e0bb5d1dd9d7f62056"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_parsec": {:hex, :nimble_parsec, "1.4.2", "8efba0122db06df95bfaa78f791344a89352ba04baedd3849593bfce4d0dc1c6", [:mix], [], "hexpm", "4b21398942dda052b403bbe1da991ccd03a053668d147d53fb8c4e0efe09c973"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
"owl": {:hex, :owl, "0.12.2", "65906b525e5c3ef51bab6cba7687152be017aebe1da077bb719a5ee9f7e60762", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "6398efa9e1fea70a04d24231e10dcd66c1ac1aa2da418d20ef5357ec61de2880"},
"req": {:hex, :req, "0.5.10", "a3a063eab8b7510785a467f03d30a8d95f66f5c3d9495be3474b61459c54376c", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "8a604815743f8a2d3b5de0659fa3137fa4b1cffd636ecb69b30b2b9b2c2559be"},
"rewrite": {:hex, :rewrite, "1.1.2", "f5a5d10f5fed1491a6ff48e078d4585882695962ccc9e6c779bae025d1f92eda", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "7f8b94b1e3528d0a47b3e8b7bfeca559d2948a65fa7418a9ad7d7712703d39d4"},
"sourceror": {:hex, :sourceror, "1.10.0", "38397dedbbc286966ec48c7af13e228b171332be1ad731974438c77791945ce9", [:mix], [], "hexpm", "29dbdfc92e04569c9d8e6efdc422fc1d815f4bd0055dc7c51b8800fb75c4b3f1"},
"spitfire": {:hex, :spitfire, "0.2.0", "0de1f519a23f65bde40d316adad53c07a9563f25cc68915d639d8a509a0aad8a", [:mix], [], "hexpm", "743daaee2d81a0d8095431729f478ce49b47ea8943c7d770de86704975cb7775"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
}
75 changes: 75 additions & 0 deletions test/mix/tasks/nerves_time.install_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# SPDX-FileCopyrightText: 2025 Peter Ullrich
#
# SPDX-License-Identifier: Apache-2.0
#
defmodule Mix.Tasks.NervesTime.InstallTest do
use ExUnit.Case, async: true

import Igniter.Test

describe "install" do
test "returns an error if the vm.args.eex does not exist at the default filepath" do
assert {:error, [warning]} =
test_project()
|> Igniter.compose_task("nerves_time.install", [])
|> apply_igniter()

assert warning =~ "Required rel/vm.args.eex but it did not exist"
end

test "adds the print statement to an existing iex.exs file" do
vm_args = """
## Customize flags given to the VM: https://www.erlang.org/doc/apps/erts/erl_cmd.html

## Do not set -name or -sname here. Prefer configuring them at runtime
## Configure -setcookie in the mix.exs release section or at runtime
"""

test_project(files: %{"rel/vm.args.eex" => vm_args})
|> Igniter.compose_task("nerves_time.install", [])
|> assert_has_patch("rel/vm.args.eex", """
+ |# Allow time warps so that the Erlang system time can more closely match the
+ |# OS system time.
+ |+C multi_time_warp
+ |
+ |
""")
end

test "is noop if the erlang flag already exists in the vm.args.eex file" do
vm_args = """
## Customize flags given to the VM: https://www.erlang.org/doc/apps/erts/erl_cmd.html

## Do not set -name or -sname here. Prefer configuring them at runtime
## Configure -setcookie in the mix.exs release section or at runtime

# Allow time warps so that the Erlang system time can more closely match the
# OS system time.
+C multi_time_warp
"""

test_project(files: %{"rel/vm.args.eex" => vm_args})
|> Igniter.compose_task("nerves_time.install", [])
|> assert_unchanged("rel/vm.args.eex")
end

test "adds the print statement to an vm.args.eex file at a provided filepath" do
vm_args = """
## Customize flags given to the VM: https://www.erlang.org/doc/apps/erts/erl_cmd.html

## Do not set -name or -sname here. Prefer configuring them at runtime
## Configure -setcookie in the mix.exs release section or at runtime
"""

test_project(files: %{"somewhere/vm.args.eex" => vm_args})
|> Igniter.compose_task("nerves_time.install", ["--file", "somewhere/vm.args.eex"])
|> assert_has_patch("somewhere/vm.args.eex", """
+ |# Allow time warps so that the Erlang system time can more closely match the
+ |# OS system time.
+ |+C multi_time_warp
+ |
+ |
""")
end
end
end
5 changes: 0 additions & 5 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,4 @@
socket_path = Path.join(System.tmp_dir!(), "nerves_time_comm")
File.rm(socket_path)

# Always warning as errors
if Version.match?(System.version(), "~> 1.10") do
Code.put_compiler_option(:warnings_as_errors, true)
end

ExUnit.start()