From a5826fd753e227f75fb822ee518600778ae1fe4e Mon Sep 17 00:00:00 2001 From: Masatoshi N Date: Tue, 18 Jul 2023 19:09:07 -0400 Subject: [PATCH] read default from a file and allow extra tips --- config/default_tips.txt | 75 ++++++++++++++++ lib/nerves_motd.ex | 19 +++-- lib/nerves_motd/tips.ex | 152 ++++----------------------------- test/nerves_motd/tips_test.exs | 16 +++- test/nerves_motd_test.exs | 8 +- 5 files changed, 120 insertions(+), 150 deletions(-) create mode 100644 config/default_tips.txt diff --git a/config/default_tips.txt b/config/default_tips.txt new file mode 100644 index 0000000..083ed1f --- /dev/null +++ b/config/default_tips.txt @@ -0,0 +1,75 @@ +Compiling systems on linux or via `mix nerves.system.shell`? Don't forgot about +`make help` and other tips from the Buildroot manual (thanks @ericr3r) +https://buildroot.org/downloads/manual/manual.html#make-tips + +Configuring the Elixir Logger to show SASL reports can help debug unexpected +GenServer restarts. +https://hexdocs.pm/logger/Logger.html#module-erlang-otp-integration + +Erlinit is a small program that starts the Erlang VM on boot. It has many +options - especially for debugging startup issues. +https://hexdocs.pm/nerves/advanced-configuration.html#overriding-erlinit-config-from-mix-config + +Get all of the default packages for starting a new Nerves project by depending +on `:nerves_pack` https://github.com/nerves-project/nerves_pack + +Get some insights to Nerves internals with this high level overview of the +Nerves architecture and choice to use the BEAM VM https://youtu.be/VzOaSvTcvU4 + +Identify your Nerves devices using a unique ID that's already programmed into +the hardware using the boardid command. Details at +https://github.com/nerves-project/boardid/ and the boardid.config file. + +Make small code changes to your running application by copy/pasting Elixir +source files at the IEx prompt. + +Need more than Elixir logs? Run `dmesg` to see log messages from device drivers +and the Linux kernel. Nerves also routes kernel log messages to the Elixir +logger. + +Nerves automatically reboots devices when the Erlang VM exits. This can make +some debugging harder, but you can easily disable it to let those sessions hang +for debugging + +Nerves enables hardware watchdogs and connects them to Erlang's heart feature +to detect and recover from the Erlang VM hanging. See +https://embedded-elixir.com/post/2018-12-10-heart/ to learn more about this. + +Nerves maintains a set of examples for common use-cases with devices. Things +like running a phoenix app, using SQLite, controlling GPIO, using Zig lang, and +more! https://github.com/nerves-project/nerves_examples + +Nerves stores all BEAM files, the Erlang runtime and various support libraries +and apps in a compressed and read-only SquashFS filesystem. The writable +application partition is mounted at `/data` + +Nerves stores all BEAM files, the Erlang runtime and various support +libraries/apps in a compressed and read-only SquashFS filesystem. Need to write +to disk? Use the application partition mounted R/W at `/data` + +Run `Nerves.Runtime.revert` to go back to the previously loaded firmware. + +See if someone has already implemented support for a sensor or other hardware +device that you have by checking https://elixir-circuits.github.io/. + +Sometimes when compiling Nerves systems, order matters! Use `make +show-build-order` while in `mix nerves.system.shell` to see the compilation +order and make sure all your 🦆🦆 are in a row (thanks @ericr3r!) + +Sometimes, you just need to see whats on your Nerves device's disk. Luckily, +there are a few ways to do that. You can even use VSCode! +https://embedded-elixir.com/post/2021-05-08-nerves-file-peeking/#sshfs + +Use `RingLogger.next` to dump the current log buffer to screen. Use +`log_attach` from `Toolshed` to attach the current session to the Elixir logger +for live logs. https://hexdocs.pm/toolshed/Toolshed.Log.html#log_attach/1 + +Use `mix firmware.unpack` to decompress a local copy of your firmware on host +and inspect the files within before installing on device + +Use the `RamoopsLogger` backend to store log messages in DRAM that can survive +unexpected reboots. https://github.com/smartrent/ramoops_logger + +Want more traditional shell tools in your Nerves IEx? Use Toolshed! Its +included by default and has may familiar functions, such as `cat`, `ls`, or +even `weather` 🌤️ https://hexdocs.pm/toolshed diff --git a/lib/nerves_motd.ex b/lib/nerves_motd.ex index eabb0fe..9b30e05 100644 --- a/lib/nerves_motd.ex +++ b/lib/nerves_motd.ex @@ -22,7 +22,11 @@ defmodule NervesMOTD do @typedoc """ MOTD options """ - @type option() :: {:logo, IO.ANSI.ansidata()} | {:extra_rows, [row()]} + @type option() :: + {:logo, IO.ANSI.ansidata()} + | {:extra_rows, [row()]} + | {:show_tip, boolean()} + | {:extra_tips, [String.t()]} @typedoc """ One row of information @@ -48,7 +52,8 @@ defmodule NervesMOTD do an empty logo (`""`) to remove it completely. * `:extra_rows` - a list of custom rows or a callback for returning rows. The callback can be a 0-arity function reference or MFArgs tuple. - * `:show tip` - a boolean flag to show the tip of the day. + * `:show_tip` - a boolean flag to show the tip of the day. + * `:extra_tips` - additional custom tips as a list of strings. """ @spec print([option()]) :: :ok def print(opts \\ []) do @@ -87,14 +92,10 @@ defmodule NervesMOTD do @spec tips([option()]) :: IO.ANSI.ansidata() defp tips(opts) do if opts[:show_tip] do - content = Tips.random() |> String.trim() - """ - ========================================================================== - [Tip of the day] - -------------------------------------------------------------------------- - #{content} - ========================================================================== + + === Tip of the day === + #{Tips.random(opts)} """ else [] diff --git a/lib/nerves_motd/tips.ex b/lib/nerves_motd/tips.ex index ff93e1a..4d30d99 100644 --- a/lib/nerves_motd/tips.ex +++ b/lib/nerves_motd/tips.ex @@ -1,147 +1,29 @@ defmodule NervesMOTD.Tips do @moduledoc false - @tips [ - """ - Want more traditional shell tools in your Nerves IEx? - - Use Toolshed! - - Its included by default and has may familiar functions, such as `cat`, - `ls`, or even `weather` 🌤️ - - https://hexdocs.pm/toolshed - """, - """ - Need more than Elixir logs? Run `dmesg` to see log messages from device - drivers and the Linux kernel. Nerves also routes kernel log messages to - the Elixir logger. - """, - """ - Erlinit is a small program that starts the Erlang VM on boot. It has many - options - especially for debugging startup issues. - - https://hexdocs.pm/nerves/advanced-configuration.html#overriding-erlinit-config-from-mix-config - """, - """ - Nerves automatically reboots devices when the Erlang VM exits. This can - make some debugging harder, but you can easily disable it to let those - sessions hang for debugging - """, - """ - Use `mix firmware.unpack` to decompress a local copy of your firmware on - host and inspect the files within before installing on device - """, - """ - See if someone has already implemented support for a sensor or other - hardware device that you have by checking - https://elixir-circuits.github.io/. - """, - """ - Nerves stores all BEAM files, the Erlang runtime and various support - libraries/apps in a compressed and read-only SquashFS filesystem. - - Need to write to disk? Use the application partition mounted R/W at - `/data` - """, - """ - Configuring the Elixir Logger to show SASL reports can help debug - unexpected GenServer restarts. - - https://hexdocs.pm/logger/Logger.html#module-erlang-otp-integration - """, - """ - Sometimes, you just need to see whats on your Nerves device's disk. - Luckily, there are a few ways to do that. You can even use VSCode! - - https://embedded-elixir.com/post/2021-05-08-nerves-file-peeking/#sshfs - """, - """ - Get all of the default packages for starting a new Nerves project by - depending on `:nerves_pack` - - https://github.com/nerves-project/nerves_pack - """, - """ - Nerves enables hardware watchdogs and connects them to Erlang's heart - feature to detect and recover from the Erlang VM hanging. - - See https://embedded-elixir.com/post/2018-12-10-heart/ to learn more - about this. - """, - """ - Make small code changes to your running application by copy/pasting - Elixir source files at the IEx prompt. - """, - """ - Nerves stores all BEAM files, the Erlang runtime and various support - libraries and apps in a compressed and read-only SquashFS filesystem. - - The writable application partition is mounted at `/data` - """, - """ - Identify your Nerves devices using a unique ID that's already programmed - into the hardware using the boardid command. Details at - https://github.com/nerves-project/boardid/ and the boardid.config file. - """, - """ - Nerves maintains a set of examples for common use-cases with devices. - Things like running a phoenix app, using SQLite, controlling GPIO, using - Zig lang, and more! - - https://github.com/nerves-project/nerves_examples - """, - """ - Use `RingLogger.next` to dump the current log buffer to screen. - - Use `log_attach` from `Toolshed` to attach the current session to the - Elixir logger for live logs. - - https://hexdocs.pm/toolshed/Toolshed.Log.html#log_attach/1 - """, - """ - Get some insights to Nerves internals with this high level overview of - the Nerves architecture and choice to use the BEAM VM - - https://youtu.be/VzOaSvTcvU4 - """, - """ - Run `Nerves.Runtime.revert` to go back to the previously loaded firmware. - """, - """ - Compiling systems on linux or via `mix nerves.system.shell`? - - Don't forgot about `make help` and other tips from the Buildroot manual - (thanks @ericr3r) - - https://buildroot.org/downloads/manual/manual.html#make-tips - """, - """ - Sometimes when compiling Nerves systems, order matters! - - Use `make show-build-order` while in `mix nerves.system.shell` to see the - compilation order and make sure all your 🦆🦆 are in a row (thanks - @ericr3r!) - """, - """ - Use the `RamoopsLogger` backend to store log messages in DRAM that can - survive unexpected reboots. - - https://github.com/smartrent/ramoops_logger - """ - ] + @divider "\n\n" + @default_tips File.read!("config/default_tips.txt") + |> String.split(@divider, trim: true) + |> Enum.reject(&(&1 == "")) @type tip :: String.t() @doc """ - List all Nerves tips + List all tips """ - @spec all() :: [tip] - def all(), do: @tips + @spec all(keyword) :: [tip] + def all(opts \\ []) do + extra_tips = Keyword.get(opts, :extra_tips, []) + @default_tips ++ extra_tips + end @doc """ - Pick one Nerves tip randomly + Pick one tip randomly """ - @spec random() :: tip - def random(), do: Enum.random(@tips) + @spec random(keyword) :: tip + def random(opts \\ []) do + all(opts) + |> Enum.reject(&(&1 == "")) + |> Enum.random() + end end diff --git a/test/nerves_motd/tips_test.exs b/test/nerves_motd/tips_test.exs index 9843b62..643fe11 100644 --- a/test/nerves_motd/tips_test.exs +++ b/test/nerves_motd/tips_test.exs @@ -3,12 +3,22 @@ defmodule NervesMOTD.TipsTest do alias NervesMOTD.Tips doctest NervesMOTD.Tips - test "all returns a list of strings" do + @extra_tip """ + There are only two hard things in Computer Science: cache invalidation and naming things. + """ + + test "all/1 returns a list of default tips" do assert [tip | _] = Tips.all() assert is_binary(tip) end - test "random returns a string" do - assert Tips.random() |> is_binary() + test "all/1 with extra tips returns tips that contain extra tips" do + tips = Tips.all(extra_tips: [@extra_tip]) + assert @extra_tip in tips + end + + test "random/1 returns a string" do + tip = Tips.random(extra_tips: [@extra_tip]) + assert is_binary(tip) end end diff --git a/test/nerves_motd_test.exs b/test/nerves_motd_test.exs index 89aa489..91f5e56 100644 --- a/test/nerves_motd_test.exs +++ b/test/nerves_motd_test.exs @@ -358,12 +358,14 @@ defmodule NervesMOTDTest do :ok end - test "does nothing by default" do - refute capture_motd() =~ ~r/Tip of the day/ + @tip_of_the_day_regex ~r/Tip of the day/ + + test "does not show tip by default" do + refute capture_motd() =~ @tip_of_the_day_regex end test "shows tip when enabled" do - assert capture_motd(show_tip: true) =~ ~r/Tip of the day/ + assert capture_motd(show_tip: true) =~ @tip_of_the_day_regex end end end